从简单例子深入理解hashMap的put和get原理

先来看一个简单的例子:

HashMap map = new HashMap();

map.put("语文", 1);
map.put("数学", 2);
map.put("英语", 3);
map.put("历史", 4);
map.put("政治", 5);
map.put("地理", 6);
map.put("生物", 7);
map.put("化学", 8);

for(Entry entry : map.entrySet()) {

    System.out.println(entry.getKey() + ": " + entry.getValue());

}

运行结果是:

从简单例子深入理解hashMap的put和get原理_第1张图片从简单例子深入理解hashMap的put和get原理_第2张图片

 

 

可以看到:

1. 有一个叫做table大小是16的Entry数组,16即为HashMap初始容量。

 

2. 这个table数组存储了Entry类的对象。HashMap类有一个叫做Entry的内部类。这个Entry类包含了key-value作为实例变量

3. 每当往hashmap里面存放key-value对的时候,都会为它们实例化一个Entry对象,这个Entry对象就会存储在前面提到的Entry数组table中。示例中创建的Entry对象将会存放的位置(即在table中的精确位置),是根据key的hashcode()方法计算出来的hash值来决定。hash值用来计算key在Entry数组的索引。

从简单例子深入理解hashMap的put和get原理_第3张图片

 

4.观察上面的断点视图中数组的索引 0,它有一个叫做HashMap$Entry的Entry对象。

上面的对象的key-value的hash值是如何计算出来的?根据key的hashcode()方法。

put源码:

 

从简单例子深入理解hashMap的put和get原理_第4张图片

分析:

1. 对key做null检查。如果key是null,会被存储到table[0],因为null的hash值总是0。

2. key的hashcode()方法会被调用,然后计算hash值。hash值用来找到存储Entry对象的数组的索引。

3. indexFor(hash,table.length)用来计算在table数组中存储Entry对象的精确的索引。

4. 在我们的例子中已经看到,如果两个key有相同的hash值(也叫冲突),他们会以链表的形式来存储。所以,这里我们就迭代链表。

*如果在刚才计算出来的索引位置没有元素,直接把Entry对象放在那个索引上。

*如果索引上有元素,然后会进行迭代,一直到Entry->next是null。当前的Entry对象变成链表的下一个节点。

(这也是为什么上面的例子中,放了8个元素,却显示了6个。)

put方法的思路:

1.对 key 的 hashCode()做 hash,然后再计算 index;

2.如果没碰撞直接放到 bucket 里;

3.如果碰撞了,以链表的形式存在 buckets 后;

4.如果碰撞导致链表过长(大于等于 TREEIFY_THRESHOLD),就把链表转换成红黑树;

5.如果节点已经存在就替换 old value(保证 key 的唯一性)

6.如果 bucket 满了(超过 load factor * current capacity),就要 resize。

从简单例子深入理解hashMap的put和get原理_第5张图片

 

*如果我们再次放入同样的key会怎样呢?逻辑上,它应该替换老的value。事实上,它确实是这么做的。在迭代的过程中,会调用equals()方法来检查key的相等性(key.equals(k)),如果这个方法返回true,它就会用当前Entry的value来替换之前的value。

get源码:

从简单例子深入理解hashMap的put和get原理_第6张图片

 

get工作原理分析:

当你传递一个key从hashmap总获取value的时候:

 1. 对key进行null检查。如果key是null,table[0]这个位置的元素将被返回。

 2. key的hashcode()方法被调用,然后计算hash值。

 3. indexFor(hash,table.length)用来计算要获取的Entry对象在table数组中的精确的位置,使用刚才计算的hash值。

 4. 在获取了table数组的索引之后,会迭代链表,调用equals()方法检查key的相等性,如果equals()方法返回true,get方法返回         Entry对象的value,否则,返回null。

HashMap中两个重要的参数:

容量(Capacity)和负载因子(Load factor)

简单的说,Capacity就是buckets的数目,Load factor就是buckets填满程度的最大比例。如果对迭代性能要求很高的话不要把capacity设置过大,也不要把load factor设置过小。当bucket填充的数目(即hashmap中元素的个数)大于capacity*load factor时就需要调整buckets的数目为当前的2倍。

 

 

你可能感兴趣的:(java)