zoukankan      html  css  js  c++  java
  • HashMap

    HashMap

    1.为什么hashmap长度必须是2的幂

    hashmap插入源码代码如下

    p = tab[i = (n - 1) & hash]
    

    数据的hash值一般都很长(各数据类型hashCode源码解读在这里),hashmap需要根据hash值将数据插入到对应的桶中,

    例如某个数的hash值是011001101010,若hashmap长度为16,那么hashMap中的桶编号就是0-15(0000-1111),二进制表示需要4位,那么hashmap只需要hash值的后四位作为桶编号,

    如果hashmap的长度是2的幂,长度减1刚好是覆盖所有桶编号且全为1的二进制数,全为1的二进制数与hash值作与运算不会对原有值做任何改动,只是取出hash值的后n位.

    例如hashmap长度为16,那么16-1=15=1111,1111与任何数作与运算都只会取出其后四位.

    但若数组长度为15,hashcode值会与14(1110)进行“与”,那么最后一位永远是0,编号为0001,0011,0101,1001,1011,0111,1101的桶永远都不会存放元素,空间浪费相当大

    也可以使用取余%运算,那样就不用考虑2的幂,但是%运算速度是很慢的,考虑到hashmap经常查存,性能会下降很多

    2.为什么默认初始长度是16

    前面我们说了为什么hashmap大小一定是2的幂,hashmap大小是8或者4的话很容易导致map扩容影响性能,分配的太大的话又会浪费资源,所以就使用16作为初始大小

    3.为什么每次长度增加是乘2

    问题1已经回答了

    4.如果自定义初始长度不是2的次方,冲突会增多吗

    不会,hashmap大小会自动调节成为2的幂

    static final int tableSizeFor(int cap) {
    int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
    /*
    numberOfLeadingZeros求出int值的前n个0的个数
    java内的int为32位
    假设传入cap=13,cap-1=12 二进制即为1100 计算机内表示为28个0+1100
    那么numberOfLeadingZeros值即为28
    -1在计算机内补码表示为32个1
    >>>表示无符号右移,就是把-1的二进制带上符号位右移
    那么-1右移28位结果就是1111=15,结果返回15+1=16
    */
    

    5.为什么长度到达8时变为红黑树

    源码内有解读,hashmap在良好的hash算法条件下,值分布时符合泊松分布的,遇到单个桶内元素超过8的概率很小(一个桶内出现8的元素的概率为0.00000006)(为什么不会是数据太多导致单个桶内元素超过8呢?解答在下面),所以说遇到这种情况大多是因为hashcode函数写的很糟糕,一旦出现超过8的情况,估计这个桶还会增加更多的元素.hashmap不可能去改变用户的hashcode代码,只能使用红黑树对性能进行优化.

    为什么不会是数据太多导致单个桶内元素超过8呢,因为装填因子为0.75,假设hashmap大小为n,若hashmap中有超过n*0.75个桶存了元素,就会扩容为原有大小的两倍.良好的hashcode函数不会让多个元素插入一个桶中.

    6.为什么长度变为6时才重新变为链表

    个人看法:提供一个缓冲区间,假设只有一个阈值8,现有一个桶有7个元素,若刚好不停的对这个桶进行增删增删增删,那么这个桶就会不停的在链表和红黑树之间切换,十分影响效率

    7.为什么不直接使用红黑树

    1.红黑树太占空间

    源码中叙述如下

    Because TreeNodes are about twice the size of regular nodes, we use them only when bins contain enough nodes 
    

    2.在良好的hash算法条件下,桶元素超过8的概率很小,节点较少时,链表和红黑树查找速度相差不大

    以桶元素为7举例,顺序查找ASL为(7+1)/2=4,

    红黑树ASL为lg7<3

    8.链表长度达到8就立刻变为红黑树吗

    不是,源码如下

    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                resize();
    

    在转变成树之前,还会有一次判断,只有键值对数量大于 64 才会发生转换。这是为了避免在哈希表建立初期,多个键值对恰好被放入了同一个链表中而导致不必要的转化。

    9.为什么装填因子是0.75

    选择0.75作为默认的加载因子,完全是时间和空间成本上寻求的一种折衷选择

    加载因子设置为0.75而不是1,是因为设置过大,桶中键值对碰撞的几率就会越大,同一个桶位置可能会存放好几个value值,这样就会增加搜索的时间,性能下降,

    设置过小也不合适,如果是0.1,那么10个桶,threshold为1,你放两个键值对就要扩容,太浪费空间了

    而且源码内有解读,hashmap在良好的hash算法条件下,值分布时符合泊松分布的,遇到单个桶内元素超过8的概率很小(一个桶内出现8的元素的概率为0.00000006)

    10 为什么使用红黑树而不是AVL

    理论上红黑树牺牲了一些查找性能 但其本身并不是完全平衡的二叉树。因此插入删除操作效率略高于AVL树.
    AVL树用于自平衡的计算牺牲了插入删除性能,但是因为最多只有一层的高度差,查询效率会高一些.

  • 相关阅读:
    UVA1627-Team them up!(二分图判断+动态规划)
    UVA10817-Headmaster's Headache(动态规划基础)
    UVA1626-Brackets sequence(动态规划基础)
    UVA11584-Partitioning by Palindromes(动态规划基础)
    UVA11584-Partitioning by Palindromes(动态规划基础)
    UVA11400-Lighting System Design(动态规划基础)
    UVA12563-Jin Ge Jin Qu hao(动态规划基础)
    UVA116-Unidirectional TSP(动态规划基础)
    JavaScriptCore框架在iOS7中的对象交互和管理
    iOS7新JavaScriptCore框架入门介绍
  • 原文地址:https://www.cnblogs.com/INnoVationv2/p/12495468.html
Copyright © 2011-2022 走看看