HashMap实现了Map接口,并继承 AbstractMap 抽象类,其中 Map 接口定义了键值映射规则。AbstractMap 抽象类提供了 Map 接口的骨干实现,以最大限度地减少实现Map接口所需的工作。

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable{
...
}

初始容量 和 负载因子,这两个参数是影响HashMap性能的重要参数。其中,容量表示哈希表中桶的数量 (table 数组的大小),初始容量是创建哈希表时桶的数量;负载因子是哈希表在其容量自动增加之前可以达到多满的一种尺度,它衡量的是一个散列表的空间的使用程度,负载因子越大表示散列表的装填程度越高,反之愈小。

哈希的相关概念

  Hash 就是把任意长度的输入(又叫做预映射, pre-image),通过哈希算法,变换成固定长度的输出(通常是整型),该输出就是哈希值。这种转换是一种 压缩映射 ,也就是说,散列值的空间通常远小于输入的空间。不同的输入可能会散列成相同的输出,从而不可能从散列值来唯一的确定输入值。简单的说,就是一种将任意长度的消息压缩到某一固定长度的息摘要函数。

 1  /**
 2      * Constructs an empty HashMap with the default initial capacity
 3      * (16) and the default load factor (0.75).
 4      */
 5     public HashMap() {
 6
 7         //负载因子:用于衡量的是一个散列表的空间的使用程度
 8         this.loadFactor = DEFAULT_LOAD_FACTOR;
 9
10         //HashMap进行扩容的阈值,它的值等于 HashMap 的容量乘以负载因子
11         threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
12
13         // HashMap的底层实现仍是数组,只是数组的每一项都是一条链
14         table = new Entry[DEFAULT_INITIAL_CAPACITY];
15
16         init();
17     }
18
19
20
21 static class Entry<K,V> implements Map.Entry<K,V> {
22
23     final K key;     // 键值对的键
24     V value;        // 键值对的值
25     Entry<K,V> next;    // 下一个节点
26     final int hash;     // hash(key.hashCode())方法的返回值
27
28     /**
29      * Creates new entry.
30      */
31     Entry(int h, K k, V v, Entry<K,V> n) {     // Entry 的构造函数
32         value = v;
33         next = n;
34         key = k;
35         hash = h;
36     }
37
38     ......
39
40 }
View Code

其中,Entry为HashMap的内部类,实现了 Map.Entry 接口,其包含了键key、值value、下一个节点next,以及hash值四个属性。事实上,Entry 是构成哈希表的基石,是哈希表所存储的元素的具体形式。

在 HashMap 中,键值对的存储是通过 put(key,vlaue) 方法来实现的,其源码如下:

 1 public V put(K key, V value) {
 2
 3         //当key为null时,调用putForNullKey方法,并将该键值对保存到table的第一个位置 
 4         if (key == null)
 5             return putForNullKey(value);
 6
 7         //根据key的hashCode计算hash值
 8         int hash = hash(key.hashCode());             //  ------- (1)
 9
10         //计算该键值对在数组中的存储位置(哪个桶)
11         int i = indexFor(hash, table.length);              // ------- (2)
12
13         //在table的第i个桶上进行迭代,寻找 key 保存的位置
14         for (Entry<K,V> e = table[i]; e != null; e = e.next) {      // ------- (3)
15             Object k;
16             //判断该条链上是否存在hash值相同且key值相等的映射,若存在,则直接覆盖 value,并返回旧value
17             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
18                 V oldValue = e.value;
19                 e.value = value;
20                 e.recordAccess(this);
21                 return oldValue;    // 返回旧值
22             }
23         }
24
25         modCount++; //修改次数增加1,快速失败机制
26
27         //原HashMap中无该映射,将该添加至该链的链头
28         addEntry(hash, key, value, i);
29         return null;
30     }
View Code
01-19 20:25