数据结构-散列表

日常注意

hashMap的扩容存在大量rehash,注意如果知道需存放容量,一次性设置初始容量。
避免16的反复扩容

加载因子0.75属于泊松分布,没特殊必要不要骚改。

乘积值

public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }

获取hashCode 31作为乘积值,原因是减少Hash碰撞概率

扰动函数

static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }

(h = key.hashCode()) ^ (h >>> 16)。把哈希值右移16位,也就正好是自己长度的一半,之后与原哈希值做异或运算,这样就混合了原哈希值中的高位和低位,增大了随机性

初始化容量和负载因子

散列数组需要一个2的倍数的长度,因为只有2的倍数在减1的时候,才会出现01111这样的值

static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }

如果传入奇数的初始容量,最终会转换为最临近的2的倍数的二进制

扩容元素

原哈希值与扩容新增出来的长度16,进行&运算,如果值等于0,则下标位置不变。如果不为0,那么新的位置则是原来位置上加16

题外话

特别注意HashMap Key存放对象,需要重写hashcode和equal方法
JAVA8之后,HashMap的Value链表容量超过8,是改为红黑树,减少查询的时间复杂度O(logn)

类别 key value为null
hashMap 支持
ConcurrentSkipListMap 不支持
ConcurrentHashMap 不支持
HashTable 不支持
linkedhashMap 支持
treeMap key不支持,value支持

为null存在二义性,无法边变是找不到,还是缺失value为null

Collections.synchronizedMap勉强实现同步,key value又可以为null

参考

https://www.jianshu.com/p/ecdf00e33b98
https://www.cnblogs.com/xinzhao/p/5644175.html
https://cloud.tencent.com/developer/article/1690271