从头认识java-15.7 Map(2)-介绍HashMap的工作原理-put方法

这一章节我们来介绍HashMap的工作原理。

1.HashMap的工作原理图

下图引用自:http://www.admin10000.com/document/3322.html

从头认识java-15.7 Map(2)-介绍HashMap的工作原理-put方法_第1张图片

2.HashMap初始化的时候我们可以这样理解:一个数组,每一个位置存储的是一个链表,链表里面的每一个元素才是我们记录的元素


3.下面我们来看put的源码:

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        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;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

解释:

a.当key为空的时候,通过putForNullKey方法,把元素放到最开始的位置。注意:HashMap是允许Key为空的。下面的代码就是证明:

package com.ray.ch15;

import java.util.HashMap;

public class Test {
	public static void main(String[] args) {
		HashMap<String, String> map = new HashMap<String, String>();
		map.put(null, "1");
	}
}


b.当key不为空的时候,需要计算key的hashcode,因为hashCode()方法是继承Object,因此每一个key都有这样的方法。当然,我们也可以重写hashCode(),但是这里将出现一系列的问题,我们后面的章节再展开。

注意:其中有一个hash()方法,就是把Key的hashCode再一次hash,这样做主要是为了使这个hash码更加的平均分布,下面是hash的源码:

static int hash(int h) {
        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

然后我们再通过indexFor计算出这一系列元素放在大table的什么位置。


d.当计算完链表在table上面的位置,我们就需要遍历上面的元素,因此出现了for循环。


扩展(1):这里需要突出一下上面所说的table,table的源码:

 /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry[] table;

它是一个Entry组成的数组,这个Entry就是我们放到里面的元素。

这个table,就是我们上图的左边的Y轴上面的1,2,3,4.....它每一个位置上都是记录一个Entry的链表。

从注释可以看到,这个table是可变的,当Map的容量超过某一个界限,他就会触发resize方法,从新创建一个table,然后把就的数据copy一份到新的里面去。下面是resize的源码:

void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }

        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }

扩展(2):讲到table,我们必须将另一额重要参数装载因子(DEFAULT_LOAD_FACTOR)。下面是装载因子初始化的值,0.75,代表当Map的容量达到0.75的时候,将触发resize方法,重新扩展Map的大小。这个因子的数值决定了一大部分Map 的性能,后面的章节将会说明。

/**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;


4.综上所述,Map的put 的流程是:

(1)检查key是否为空

(2)计算key的hashcode和在table里面的index(位置)

(3)找到table上面的元素

(4)遍历链表,如果没有就put进去,有就更新



总结:这一章节我们主要通过put方法来介绍HashMap的工作原理。


这一章节就到这里,谢谢。

-----------------------------------

目录


你可能感兴趣的:(java)