容器之路 HashMap、HashSet解析(一)

 

1.1 HashMap概述

相信大家在大学的时候都学习过散列表。

使用散列表的查找算法主要分为两步,第一步是利用散列函数将被查找的键转化为一个索引,理想情况下,所有不同的key都会被散列为不同的索引值,但是由于散列函数无法达到完美的散列,所以,我们通常还需要处理碰撞的情况。

处理碰撞的方法主要有两种,一种是拉链法,另一种是线性探测法。

HashMap中,使用的是拉链法,也就是一个桶,由数组构成,数组的索引代表了散列值,每个数组后接了一个链表。当出现冲突碰撞的情况时,我们将判断这个key是否已经存在,如果存在,那么,替换value值,如果不存在,那么,将这个Entry链接到链表上。

//HashMap中的桶 transient Node<K,V>[] table;

那么,当我们查找时,先找到对应的索引值,然后,遍历链表,找到所需的内容。

这里,我们需要注意,如果大量数据的散列值相同,就会导致链表很长,查找效率也就变低了。因此,在JDK1.8中,当链表的长度超出阈值,我们会将链表进行树化,形成红黑树,让查找效率提高。

还有就是,关于MapresizeMap中有一个参数叫做负载因子loadFactor,当桶中的数据量达到桶的长度乘上负载因子时,就会进行扩容。扩容过程中,会把桶的大小变为原来的2倍,并且,将原来Map中的内容进行重新散列,这部分内容也是HashMap中的一个难点,而且,这个算法十分巧妙。

下面是HashMap中节点的数据结构,是单链表结点

static class Node<K,V> implements Map.Entry<K,V> {     final int hash;     final K key;     V value;     Node<K,V> next;          Node(int hash, K key, V value, Node<K,V> next) {         this.hash = hash;         this.key = key;         this.value = value;         this.next = next;     }          public final K getKey()        { return key; }     public final V getValue()      { return value; }     public final String toString() { return key + "=" + value; }          public final int hashCode() {         return Objects.hashCode(key) ^ Objects.hashCode(value);     }          public final V setValue(V newValue) {         V oldValue = value;         value = newValue;         return oldValue;     }          public final boolean equals(Object o) {         if (o == this)             return true;         
                        
关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信