深入理解Java中的Map

Map中的key是如何确保重复验证的快速性及key值的唯一性呢?

巧妙地利用了Hash算法来实现并达到重复验证的快速性及key值的唯一性。

在解剖HashMap之间,需要先简单的了解Hash算法,Hash算法一般也成为散列算法,通过散列算法将任意的值转化成固定的长度输出,该输出就是散列值,这是一种压缩映射,也就是,散列值的空间远远小于输入的值空间。其实Hash算法产生的目的很单纯,其发明的目的是提高海量数据的查找速度。

简单的说,hash算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系,(每一个真实值只能有一个键值,但是一个键值可以对应多个真实值),这样可以快速在数组等里面存取数据。

举个实例更能说明问题:

假设数据表中有N个无序的字符串(例如:中文人名),给你一个字符串,请迅速找到它在数据表中的序号。
最笨的方法是逐个比较的方式来查找。查找时间是O(N),简单说最后的情况是比较N次。

hash 表能够加快查找速度。使用hash表首先要申请一个定长的指针数组。通过在建立数据表时通过特定的计算公式(hash散列函数)计算出每个字符串对应的一个数值(就是将不固定长度的字符串转换成一个固定长度的数值或索引值)。而后把此数值作为数组下标,把此字符串在数据表的序号保存在此数组元素中(可以扩展到保存一个对象实例指针)。将来想查找某字符串对应位置时,只需要通过hash散列函数计算出字符串对应的值就可以直接知道此字符串的序号等信息了。
这样查找时间是O(1)了。因为不需要查找了,知道数组下标就能访问数组相应元素了,而元素中保存的就是序号等信息。

不妨给你一个直观的比方吧:

张三,给你个任务,你到山东省东营区去找一下一个叫做李四的人吧。张三很老实,说了声是就走了,三个月后回来,终于找着了那个叫做李四的人。他后来跟我们说,他采用了遍历的手法,即挨家挨户的去问,去找。

而将这个任务分配给王五的时候,王五说,老板,你给个确切的地址吧。老板说:山东省东营市东营区xx街道办事处xx社区xx号。结果不到一天时间,王五便找着了。

这也许就是hash查找算法与普通查找算法的区别。

通过查看HashMap的源代码证实,该HashMap确实采用了Hash算法来提高查找key的速度,并且使用了equals()来判断是否重复,有代码为证:

hash

static int hash(int h) {
 h ^= (h >>> 20) ^ (h >>> 12);
 return h ^ (h >>> 7) ^ (h >>> 4);
 }

put:

public V put(K key, V value) {
 if (key == null)
 return putForNullKey(value);
 int hash = hash(key.hashCode());
 int i = indexFor(hash,table.length);
 for (Entry<</SPAN>K,V> e = table[i];e != null;e = e.next) {
 Objectk;
 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
 VoldValue = e.value;
 e.value = value;
 e.recordAccess(this);
 return oldValue;
 }
 }

 modCount++;
 addEntry(hash,key, value, i);
 return null;
 }

HashMapput原理是这样的:
1
、首先对key采用hashCode()方法进行散列化,就是将key转换生成一个int值,相同的key肯定会生成相同的int值,并对该int值进行hash计算得到hash值。
2
、通过hash值得到Entry数组的下标,然后通过该下标,得到已经存入的数据,将已经存入的数据的keyhash进行比对,若相同证明是重复,则忽略。
3
、若不相同,则通过addentry()方法将数据存入该数组的下标中,同时存入的还有keyhash值。

用一句话说来就是,通过待存入的keyhash值计算出数组的下标,并根据该下标提取已经存入的值,将两者进行比对,若相同则忽略,不同则put进去。

现在知道为什么Map中的key是唯一性的原因了吧?这与Mapput时兢兢业业检查的努力是分不开的。

假设有一个key1value"zhangSan",经过hash之后成为101,那么这个101就作为数组的下标,然后将hash=101key=1value="zhangSan"的值封装成实体对象存放到该数组的101下标处。因为不同的key会产生不同的hash值,这也是为什么HashMap不排序的原因!

get

public Vget(Object key) {
 if (key == null)
 return getForNullKey();
 int hash = hash(key.hashCode());
 for (Entry<</SPAN>K,V> e = table[indexFor(hash,table.length)];
 e != null;
 e = e.next) {
 Objectk;
 if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
 return e.value;
 }
 return null;
 }

那么通过key提取值时,当然先要通过该key计算出hash值来,再通过这个hash值作为下标提取出对应的实体对象所容纳的value来。
同时加了必要的判断来确保提取出正确的数值来。

哈哈,在一个确定的城市里,领到了一个确定的门牌号,相比在茫茫人海中漫无目的的捞针,知道为何提取数据如何之快了吧!

Hash算法的存取速度要比数组差一些,但是比起单纯的链表,在查找和存取方面却要好多。

猜你喜欢

转载自blog.csdn.net/G1248019684/article/details/51377661
今日推荐