HashMap的一些小知识

Q: hashMap的初始容量为什么时16.

A: DEFAULT_INITIAL_CAPACITY为16, hash因用位运算。特定2n。所以map容量需要是2n。而16应该是比较合适的大小。


MAXIMUM_CAPACITY最大数组元素数量,2^30。

DEFAULT_LOAD_FACTOR 负载因子。默认为0.75

Q: hash?

A:(hashcode ^ hashcode<<<16) & (n-1)

获取小于n-1 低位掩码。

为了减少冲突。 高低位异或,让高位参与运算。

Q: 扩容条件?

A:

1.7: 1、 存放新值的时候当前已有元素的个数必须大于等于阈值

2、 存放新值的时候当前存放数据发生hash碰撞(当前key计算的hash值换算出来的数组下标位置已经存在值)

1.8:a 当前存入数据大于阈值即发生扩容

b 存入数据到某一条链表上,此时数据大于8,且总数量小于64即发生扩容

Q: 为什么不是1 或者 0.5?

当负载因子为1时,将数组填充满才会发生扩容,虽然提高了空间利用率,但会出现很多链表已经形成红黑树的情况。会增加查询时间增大。
当负载因子为0.5时,当hash后的值超过length/2.发生扩容,频繁的扩容使耗时降低,但有相当多的空间浪费。
负载因子的值时在hashCode分布较好时,降低tree结构产生。当负载因子为0.75时,桶中节点的分布频率服从参数为0.5的泊松分布。

What is the significance of load factor in HashMap?

计算0.5的泊松分布。

Q:为什么HashMap桶中元素个数大于8转换成红黑树?

A: TreeNode占用空间是Node的两倍。所以尽量不希望Node变成TreeNode。而随着链表越来越长。查询时间复杂度为O(n),需要降低耗时,将Node转换成TreeNode。查询时间复杂度为O(log(n)). hash碰撞观察8层节点个概率为0.00000006。

Q: 为什么HashMap树的退化的阈值是6?

A:首先退化阈值从8递减。 阈值为8的情况:建树与退化循环进行。阈值为7的情况:当树中有8个节点时,删除一个节点就进行退化,增加一个节点就进行建树。频繁的变换形态会浪费大量的时间,降低性能。

Q:有没有大于TREEIFY_THRESHOLD时,不建树的情况?

A: 有!当容量小于MIN_TREEIFY_CAPACITY时,仍然使用链表形式存储。

   final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)// 初始化数组
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null) //当前桶无节点创建节点
            tab[i] = newNode(hash, key, value, null);
        else {// hash冲突
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))//k与节点相同
                e = p;
            else if (p instanceof TreeNode)//如果节点是TreeNode将value put进去
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {//链表赋值
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 阈值大于7,建树
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))//与put中key的hash值相同,跳出
                        break;
                    p = e;
                }
            }
            if (e != null) { // 如果存在key更新value
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)// 重散列
            resize();
        afterNodeInsertion(evict);
        return null;
    }

Q: 说下put?

A: 1.如果未put,首先初始化数组。2.当前桶无元素,新建节点。3.1 当前节点key与put的key相同获取节点。3.2 当前node是treeNode,putTreeVal 3.3 循环链表,找到最后一个节点或者与put的key相同的节点并获取。4. 更新节点。5. 若是size大于阈值,重散列。

final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {// 表不为空,桶不为空
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))//桶中第一个是否匹配
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)//是否为树结构
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);//循环链表获取节点
            }
        }
        return null;
    }

Q:说下get?

A:1.判断表、桶是否为空。2. 判断桶中第一个元素是否匹配。3.判断是否为书结构。4. 循环链表或者查找树获取节点。

Q:hashMap有什么线程安全问题?

A:1.7 会有成环问题。因为put时先扩容再插入。1.8会有数据丢失问题。因为put时先插入后扩容。

Q:hashMap put时,元素的位置?

A: 1.7 插头。1.8插尾。

Q: 红黑树特性。

A: 很节点为黑。子节点为黑色。 如果有其余节点为红色,子节点为黑色。 每个叶子节点到根节点之间的黑色节点个数是相同的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值