HashMap总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap总结相关的知识,希望对你有一定的参考价值。

1. 数据存储、类型:

    数组(table):没有冲突的数据存放至数组中。

    链表:冲突的数据形成一条单向链表,先put的在尾部后put的在头部,头部存至table中。

    数据类型:java.util.HashMap.Entry<K, V> implements Map.Entry<K,V>,属性有:key、value、next、hash。

2. 加载因子、threshold、扩容:

    加载因子:哈希表在其容量自动扩容之前可以达到多满的一个阈值,初始值为0.75。

    threshold=容量 * 加载因子。

    扩容:扩容后的table长度为原有的2倍,扩容条件如下:

if ((size >= threshold) && (null != table[bucketIndex])) {
	resize(2 * table.length);
	hash = (null != key) ? hash(key) : 0;
	bucketIndex = indexFor(hash, table.length);
}

    影响HashMap 性能的两个参数:初始容量和加载因子,尤其加载因子,如果加载因子过低就会增加table扩容次数并且数组利用率降低。如果过高则降低其查找性能(put、get)。

3. key的hash值的产生:

    基于key的hashCode()方法产生数值,然后基于此数值计算得到最终的hash值;如果key为String类型则通过sun.misc.Hashing.stringHash32()方法直接产生最终hash值,源码如下:

final int hash(Object k) {
	int h = hashSeed;
	if (0 != h && k instanceof String) {
		return sun.misc.Hashing.stringHash32((String) k);
	}

	h ^= k.hashCode();

	// 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);
}

4. put:

  1. 根据key计算出hash值(当然了如果key为空特殊处理)。

  2. 根据hash值和table的长度计算此key应该存储至table的下标值index。

  3. 判断table的index元素是否为null:

  4.     如果为null,生成Entry存储至数组中下标为index的位置。

  5.     如果不为null并且hash值相等而且key也相同则用新的value替换老的value并返回老的value;如果key的hash值相等但是key不同则冲突发生,此时将形成一个链表,新来的作为头,之前的作为头的next。

  6. 相关源码如下:

  7. public V put(K key, V value) {
    	if (table == EMPTY_TABLE) {
    		inflateTable(threshold);
    	}
    	if (key == null)
    		return putForNullKey(value);
    	int hash = hash(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;
    		}
    	}
    
    	modCount++;
    	addEntry(hash, key, value, i);
    	return null;
    }
    
    static int indexFor(int h, int length) {
    	// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";
    	return h & (length-1);
    }
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
    	if ((size >= threshold) && (null != table[bucketIndex])) {
    		resize(2 * table.length);
    		hash = (null != key) ? hash(key) : 0;
    		bucketIndex = indexFor(hash, table.length);
    	}
    
    	createEntry(hash, key, value, bucketIndex);
    }
    
    void createEntry(int hash, K key, V value, int bucketIndex) {
    	Entry<K,V> e = table[bucketIndex];
    	table[bucketIndex] = new Entry<>(hash, key, value, e);
    	size++;
    }

5. get:

    计算index的过程和put类似,然后判断table[index]是否为null如果是直接返回null,否则遍历以table[index]为头的链表,直到找到key的hash值相等并且key相同的Entry,然后返回其value值。相关源码:

public V get(Object key) {
	if (key == null)
		return getForNullKey();
	Entry<K,V> entry = getEntry(key);

	return null == entry ? null : entry.getValue();
}

final Entry<K,V> getEntry(Object key) {
	if (size == 0) {
		return null;
	}

	int hash = (key == null) ? 0 : hash(key);
	for (Entry<K,V> e = table[indexFor(hash, table.length)];
		 e != null;
		 e = e.next) {
		Object k;
		if (e.hash == hash &&
			((k = e.key) == key || (key != null && key.equals(k))))
			return e;
	}
	return null;
}

6. key为null时只会存在table[0]开头的链表中。


以上是关于HashMap总结的主要内容,如果未能解决你的问题,请参考以下文章

HashMap 和 ConcurrentHashMap 的区别

HashMap原理:哈希函数的设计

HashMap概述与用法总结

HashMap深度解析

python常用代码片段总结

BootStrap有用代码片段(持续总结)