JDK之Hash原理与算法

思考一下如何取得这几个数

 

 显然,用0,1判断是最快的 没有值都是0,只有有值得才是1,要【判断是否这个数在这个数组里面就直接用if(50)=1即可知道了,够简单吧】,在这100个New 出来的数组空间中,显然这种方式不需要什么二分也不需要快速查找了,

但是有一个很重要缺点就是如果是一亿大小的话,这根本不可能New 一亿出来,不过这就是最简单原始的hash即散列表,就是在数组的基础上的散列。

而同样在散列表的基础上的函数就是散列函数(即)-------------------------------hash函数

让我们再来思考思考

 

 刚刚的假设用到这里来,真的就一亿大小数组,怎样判断!

思考一办法1:取余

 

 不能用0 1页很简单就是重复会很多。】、

所以还是要10

 

 

 但是看下面这种方法还是有问题

 

 a[0]冲突了即-----------------哈希冲突,

怎么办?所以这个方法不行!

不过讲到哈希冲突了那就可以了解一下它,会发现踏实不可避免的,而且经常用的java的md5就也是理论上的会哈希冲突

看图

 

 那么肯定有问题就会有解决方案

方案一(不是重点).开放寻址

 

 

 

 

 

这样查找也很简单 ,比如要找20,从绿色的开始找,发现a[0]不仅是绿色的也是模之后值为0即a[0]=0,但是发现不是要的20所以下一位绿色,就这样找到a[2]

但是这样又有问题了那就是删除了数据怎么办,比如1没了即a[1]这时a[2]的20按照逻辑应该在a[1]去,才符合开放寻址的逻辑,这时候怎么办,Hash不能移!

看图

 

 引进一个delete即删除了都会有delete标签,就像这个绿色标签一样,查找的时候所以就是查找delete,和绿色 两项不只是绿色。

 

方法二:链表(重点)

 

 这时候的a[]就不存数据,而是key。如%10=0的即模10为0的都在 0这里找。链表的好处,详情参考数据结构链表 ,增加和插入删除等,很容易。不过!  ------遍历比较慢。

这时就引进树

 

 而为了不让查询时树也变成链表。所以引进红黑树,颜色,左右旋等操作。

那么我们的hashmap就是这样一个结构表+红黑树

首先得知道hashmap是有两个版本的jdk 1.7和1.8。而大于8的时候就引进了红黑树,7之前都是链表,为什么呢 因为Jdk经过测试,hashmap因为左右旋等操作会有消耗

在1.7里面反而更慢。在1.8才更快、看图

 

 

  那么接来下开始hashmap的常见操作

ashMap共有4个构造函数,如下:

复制代码
// 默认构造函数。
HashMap()

// 指定“容量大小”的构造函数
HashMap(int capacity)

// 指定“容量大小”和“加载因子”的构造函数
HashMap(int capacity, float loadFactor)

// 包含“子Map”的构造函数
HashMap(Map<? extends K, ? extends V> map)
复制代码

 

HashMap的API

复制代码
void                 clear() //clear() 的作用是清空HashMap。它是通过将所有的元素设为null来实现的。
Object               clone() //clone()方法的作用很简单,就是克隆一个HashMap对象并返回。
boolean              containsKey(Object key) //containsKey() 的作用是判断HashMap是否包含key返回“键为key”的键值对
boolean              containsValue(Object value) //containsValue() 的作用是判断HashMap是否包含“值为value”的元素。第一,若“value为null”,则调用containsNullValue()。第二,若“value不为null”,则查找HashMap中是否有值为value的节点。
Set<Entry<K, V>>     entrySet() //entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一个集合。我们通过entrySet()获取到的Iterator的next()方法去遍历HashMap时,实际上调用的是 nextEntry() 。而nextEntry()的实现方式,先遍历Entry(根据Entry在table中的序号,从小到大的遍历);然后对每个Entry(即每个单向链表),逐个遍历。
V                    get(Object key) //get() 的作用是获取key对应的value
boolean              isEmpty() //判断是否为0
Set<K>               keySet() //设置key
V                    put(K key, V value) //put() 的作用是对外提供接口,让HashMap对象可以通过put()将“key-value”添加到HashMap中。
void                 putAll(Map<? extends K, ? extends V> map) //putAll() 的作用是将"map"的全部元素都添加到HashMap中
V                    remove(Object key) //remove() 的作用是删除“键为key”元素
int                  size() //
Collection<V>        values()
复制代码

 

 从图中可以看出: 
(01) HashMap继承于AbstractMap类,实现了Map接口。Map是"key-value键值对"接口,AbstractMap实现了"键值对"的通用函数接口。 
(02) HashMap是通过"拉链法"实现的哈希表。它包括几个重要的成员变量:table, size, threshold, loadFactor, modCount。
  table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。 
  size是HashMap的大小,它是HashMap保存的键值对的数量。 
  threshold是HashMap的阈值,用于判断是否需要调整HashMap的容量。threshold的值="容量*加载因子",当HashMap中存储数据的数量达到threshold时,就需要将HashMap的容量加倍。
  loadFactor就是加载因子。 
  modCount是用来实现fail-fast机制的。

 

HashMap遍历方式

4.1 遍历HashMap的键值对

第一步:根据entrySet()获取HashMap的“键值对”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

复制代码
// 假设map是HashMap对象
// map中的key是String类型,value是Integer类型
Integer integ = null;
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
    Map.Entry entry = (Map.Entry)iter.next();
    // 获取key
    key = (String)entry.getKey();
        // 获取value
    integ = (Integer)entry.getValue();
}
复制代码

4.2 遍历HashMap的键

第一步:根据keySet()获取HashMap的“键”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

复制代码
// 假设map是HashMap对象
// map中的key是String类型,value是Integer类型
String key = null;
Integer integ = null;
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
        // 获取key
    key = (String)iter.next();
        // 根据key,获取value
    integ = (Integer)map.get(key);
}
复制代码

4.3 遍历HashMap的值

第一步:根据value()获取HashMap的“值”的集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

复制代码
// 假设map是HashMap对象
// map中的key是String类型,value是Integer类型
Integer value = null;
Collection c = map.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
    value = (Integer)iter.next();
}

下面通过一个实例学习如何使用HashMap

import java.util.Map;
import java.util.Random;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Collection;

/*
* @desc HashMap测试程序
*
* @author skywang
*/
public class HashMapTest {

public static void main(String[] args) {
testHashMapAPIs();
}

private static void testHashMapAPIs() {
// 初始化随机种子
Random r = new Random();
// 新建HashMap
HashMap map = new HashMap();
// 添加操作
map.put("one", r.nextInt(10));
map.put("two", r.nextInt(10));
map.put("three", r.nextInt(10));

// 打印出map
System.out.println("map:"+map );

// 通过Iterator遍历key-value
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
Map.Entry entry = (Map.Entry)iter.next();
System.out.println("next : "+ entry.getKey() +" - "+entry.getValue());
}

// HashMap的键值对个数
System.out.println("size:"+map.size());

// containsKey(Object key) :是否包含键key
System.out.println("contains key two : "+map.containsKey("two"));
System.out.println("contains key five : "+map.containsKey("five"));

// containsValue(Object value) :是否包含值value
System.out.println("contains value 0 : "+map.containsValue(new Integer(0)));

// remove(Object key) : 删除键key对应的键值对
map.remove("three");

System.out.println("map:"+map );

// clear() : 清空HashMap
map.clear();

// isEmpty() : HashMap是否为空
System.out.println((map.isEmpty()?"map is empty":"map is not empty") );
}
}


复制代码
 1 import java.util.Map;
 2 import java.util.Random;
 3 import java.util.Iterator;
 4 import java.util.HashMap;
 5 import java.util.HashSet;
 6 import java.util.Map.Entry;
 7 import java.util.Collection;
 8 
 9 /*
10  * @desc HashMap测试程序
11  *        
12  * @author skywang
13  */
14 public class HashMapTest {
15 
16     public static void main(String[] args) {
17         testHashMapAPIs();
18     }
19     
20     private static void testHashMapAPIs() {
21         // 初始化随机种子
22         Random r = new Random();
23         // 新建HashMap
24         HashMap map = new HashMap();
25         // 添加操作
26         map.put("one", r.nextInt(10));
27         map.put("two", r.nextInt(10));
28         map.put("three", r.nextInt(10));
29 
30         // 打印出map
31         System.out.println("map:"+map );
32 
33         // 通过Iterator遍历key-value
34         Iterator iter = map.entrySet().iterator();
35         while(iter.hasNext()) {
36             Map.Entry entry = (Map.Entry)iter.next();
37             System.out.println("next : "+ entry.getKey() +" - "+entry.getValue());
38         }
39 
40         // HashMap的键值对个数        
41         System.out.println("size:"+map.size());
42 
43         // containsKey(Object key) :是否包含键key
44         System.out.println("contains key two : "+map.containsKey("two"));
45         System.out.println("contains key five : "+map.containsKey("five"));
46 
47         // containsValue(Object value) :是否包含值value
48         System.out.println("contains value 0 : "+map.containsValue(new Integer(0)));
49 
50         // remove(Object key) : 删除键key对应的键值对
51         map.remove("three");
52 
53         System.out.println("map:"+map );
54 
55         // clear() : 清空HashMap
56         map.clear();
57 
58         // isEmpty() : HashMap是否为空
59         System.out.println((map.isEmpty()?"map is empty":"map is not empty") );
60     }
61 }
复制代码

 (某一次)运行结果: 

复制代码
map:{two=7, one=9, three=6}
next : two - 7
next : one - 9
next : three - 6
size:3
contains key two : true
contains key five : false
contains value 0 : false
map:{two=7, one=9}
map is empty
posted @ 2020-05-24 21:22  To_Yang  阅读(411)  评论(0编辑  收藏  举报