Redis

Hash
Hash数据结构底层实现为一个字典 dict,也是redisDb用来存储k-v的数据结构,当数据量比较小,或者单个元素比较小时,底层用ziplist存储,数据大小和元素数量阙值可以通过如下参数设置。
hash–max-ziplist-entries 512 //ziplist元素个数超过512,将改成hashtable编码
hash-max-ziplist-value 64 //单个元素大小超过64byte时,将改为hashtable编码
ziplist请添加图片描述
hashtable跟redisDb用来存储k-v的数据结构一样。

java

HashMap
数据结构
数组+链表+(红黑树jdk>=8)
DEFAULT_INITIAL_CAPACITY = 1 << 4; Hash表默认初始容量
MAXIMUM_CAPACITY = 1 << 30; 最大Hash表容量
DEFAULT_LOAD_FACTOR = 0.75f;默认加载因子
TREEIFY_THRESHOLD = 8;链表转红黑树阈值
UNTREEIFY_THRESHOLD = 6;红黑树转链表阈值
MIN_TREEIFY_CAPACITY = 64;链表转红黑树时hash表最小容量阈值,达不到优先扩容。
在这里插入图片描述

new HashMap():如果不写构造参数,默认大小是16
那么如果这地方写了new HashMap(11) 底层就给创建长度为11吗?不是,他是会有调整的,实际创建是16,那怎么转化的?
1.必须最接近size(11)
2.必须>=size
3.是2的指数次幂
size= 17, capacity=32
那么为什么是2的指数次幂呢?
底层:在计算索引的时候其实他内部用的不是%取余,而是用的位运算&,因为位运算比%效率高很多
计算索引:int i = indexFor(hash, table.length);
static int indexFor(int h, int length) {
// key.hashCode % table.lenth
return h & (table.lenth-1);
}
HashMap扩容,
当前hashmap存了多少element,size>=threshold
threshold扩容阈值 = capacity * 扩容阈值比率 0.75 = 16*0.75=12
扩容怎么扩?扩容为原来的2倍。
hash扩容,有个加载因子?loadfactor = 0.75为什么是0.75
牛顿二项式:基于空间与时间的折中考虑0.5(如果感兴趣,可以自己去网上看一下这个算法)
jdk1.7之前数据扩容后会要进行数据转移
代码

	void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) { 
                    e.hash = null == e.key ? 0 : hash(e.key);//再一次进行hash计算?
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

这种方式在转移过程存在问题,会出现转移后的数据链表成环的产生。

jdk1.8给其进行了优化,避免多线程下上面问题的产生。
引入红黑树!
只有等链表过长,阈值设置TREEIFY_THRESHOLD = 8,不是代表链表长度,链表长度>8,链表9的时候转红黑树
采用高低进行转移

Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
    do {
        next = e.next;
        if ((e.hash & oldCap) == 0) {
            //yangguo.hashcode & 16 = 0,用低位指针
            if (loTail == null)
                loHead = e;
            else
                loTail.next = e;
            loTail = e;
        }
        else {
             //yangguo.hashcode & 16 》 0 高位指针
            if (hiTail == null)
                hiHead = e;
            else
                hiTail.next = e;
            hiTail = e;
        }
    } while ((e = next) != null);
if (loTail != null) {
    loTail.next = null; 
    newTab[j] = loHead;,移到新的数组上的同样的index位置
}
if (hiTail != null) {
    hiTail.next = null;
    newTab[j + oldCap] = hiHead; //index 3+16 = 19
}

完全绕开rehash,要满足高低位移动,必须数组容量是2的幂次方

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐