hashmap底层原理1

 

HashMap源代码分析

大家在项目中很频繁的用到了java.util.HashMap 类,但是你对其内部实现是否了解呢?最近我分析了一下该类的源码,抛砖引玉,和大家分享讨论一下。

1、 HashMap的基本属性及数据结构

HashMap的基本数据结构是数组,而数组元素是链表,其元素类型是Entry。HashMap是根据对key的hash运算决定将Entry放在数组的哪个位置上的,而对于hash值相同的元素,就会放在同一个链表中。

HashMap中有一个声明为“transient Entry[] table”的属性,Entry是HashMap存储的基本数据类,其基本属性如下:

        final K key;

        V value;

        Entry<K,V> next;

        final int hash;

key和value自然不用说,hash是key的hash值,next的类型是Entry,它存在的价值就是解决hash冲突的!如果put一个key-value对时,经过hash运算,该K-V对对应的EntryA应该放在Entry[] table中第5的位置,但是该位置已经有Entry B存在了,那么就将A.next = B,A放在第5的位置上。如下图所示:


 

 

HashMap中还有几个属性:

默认容量:static final int DEFAULT_INITIAL_CAPACITY = 16;

最大容量: static final int MAXIMUM_CAPACITY = 1 << 30;

   默认加载因子: static final float DEFAULT_LOAD_FACTOR = 0.75f;

扩容因子:int threshold;(当容量超过threshold时,扩容,threshold = loadFactor* capacity

加载因子:final float loadFactor;

我们可以通过分析如下代码来了解HashMap的初始化过程:

public HashMap(int initialCapacity, float loadFactor) {

        if (initialCapacity < 0){

            throw newIllegalArgumentException("Illegal initial capacity: " + initialCapacity);}

        if (initialCapacity > MAXIMUM_CAPACITY){

            initialCapacity = MAXIMUM_CAPACITY;}

        if (loadFactor <= 0 || Float.isNaN(loadFactor)){

            throw newIllegalArgumentException("Illegal load factor: " +

                                              loadFactor);}

 

        // Find a power of 2 >= initialCapacity

        int capacity = 1;

        while (capacity < initialCapacity){

            capacity <<= 1;}

 

        this.loadFactor = loadFactor;

        threshold = (int)(capacity *loadFactor);

        table = new Entry[capacity];

        init();

}

该构造函数的参数是我们期望的初始化容量initialCapacity和装载因子loadFactor

我们通过

while (capacity <initialCapacity){

capacity <<= 1;}

这段代码可以了解到,capacity是大于initialCapacity的最小2次幂数值。也就是说,如果我们的参数initialCapacity = 10,loadFactor = 0.8,那么实际上capacity = 16,该HashMap的初始容量是16,当元素个数超过10 * 0.8 = 8的时候,map进行扩容。

int hash = hash(key.hashCode());

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

              static intindexFor(int h, int length) {

                    return h & (length-1); }

 

  
 
要注意的是,HashMap对key进行hash时,不是取的key的key.hashCode()方法,而是对 key 的hashcode作一些运算得到最后的hash值,在所有涉及到entry的操作中都要计算hash =  hash(key.hashCode()) 。有了对元素key两次hash后的hash值,又如何找到元素位于table中的哪个位置呢?

 

 

 

int hash = hash(key.hashCode());

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

              static intindexFor(int h, int length) {

                     return  h& (length-1); }

 

从上面代码可以看出,i 的值就是元素处于table中的位置,i 是由hash和length计算出来的。



 

下面来看一下HashMap中的put/get/remove方法实现。

2、put/get/remove操作如何实现

先看下put方法的源码

 

 

 

public V put(K key, V value) {

       //当key=null,调用putForNullKey方法,该方法默认将key=null的值放在table首位

        if (key == null)

            return putForNullKey(value);

       //计算hash值

        int hash = hash(key.hashCode());

       //计算存储的位置

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

       //遍历table[i]处已经存在的元素

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

            Object k;

       //如果该元素的key已经存在,则替换value值,同时返回原始值

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

                V oldValue = e.value;

                e.value = value;

                e.recordAccess(this);

                return oldValue;

            }

        }

    //如果该元素的key不存在,执行插入操作,返回null 

        modCount++;

        addEntry(hash, key, value, i);

        return null;

    }


你可能感兴趣的:(java,HashMap,Hashtable)