JDK1.7 HashMap 源码分析

HashMap是Java里基本的存储Key、Value的一个数据类型,了解它的内部实现,可以帮我们编写出更高效的Java代码。

本文主要分析JDK1.7中HashMap实现,JDK1.8中的HashMap已经和这个不一样了,后面会再总结。

正文 HashMap概述

HashMap根据键的hashCode值获取存储位置,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。 HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。

HashMap的存储结构如下图所示:

_thumb6

HashMap根据键的hashCode值和HashMap里数组的大小取余,余数即为该Key存储的数组位置。

如:一个Key的hashCode为15,HashMap的Size为6,15 % 6 = 3,所以该Key存储在数组的第三个位置。

考虑另一种情况,如果一个Key的hashCode为21,那21 % 6 = 3,所以该Key也存储在数组的第三个位置,这样岂不是重复了?

所以对于在同一个位置的Key,HashMap把他们存储在一个单向链表里,新的Key永远在最前面。

如果这个数组里存储的太满,HashMap还有扩容机制。

下面我们分析HashMap的源代码,来看看数据是怎么存储的。

PUT

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

 

public V put(K key, V value) {

    //判断如果table为空,则初始化table

    if (table == EMPTY_TABLE) {

        inflateTable(threshold);

    }

    if (key == null)

        return putForNullKey(value);

    //计算key的hash值

    int hash = hash(key);

    //根据key的hash值和table.length计算KEY的位置

    int i = indexFor(hash, table.length);

    //判断是否有重复的值,若有,则用新值替换旧值,并返回旧值

    for (Entry<K,V> e = table[i]; e != null; e = e.next) {

        Object k;

        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

            V oldValue = e.value;

            e.value = value;

            e.recordAccess(this);

            return oldValue;

        }

    }

 

    //修改的次数加一,用于迭代HashMap时,判断HashMap元素有没有修改

    modCount++;

    //添加key

    addEntry(hash, key, value, i);

    return null;

}

 

inflateTable — 初始化HashMap内部数组

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

 

private void inflateTable(int toSize) {

    //根据toSize计算容量,即大于toSize的最小的2的n次方

    int capacity = roundUpToPowerOf2(toSize);

    ………

}

 

private static int roundUpToPowerOf2(int number) {

    // assert number >= 0 : "number must be non-negative";

    return number >= MAXIMUM_CAPACITY

            ? MAXIMUM_CAPACITY

            : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;

}

 

public static int highestOneBit(int i) {

    // HD, Figure 3-1

    i |= (i >>  1);

    i |= (i >>  2);

    i |= (i >>  4);

    i |= (i >>  8);

    i |= (i >> 16);

    return i - (i >>> 1);

}

 

关键方法Integer.highestOneBit((number - 1) << 1),这个方法的结果就是求出大于给定数值的,最小的2的N次方。

解释之前先说明几个概念:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/c738de738aaab05c3780f9308d62a528.html