jdk1.7 1.8 hash map 区别及一些细节

1 扩容

1.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);重点
                }
                int i = indexFor(e.hash, newCapacity);重点
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

 

1.8

                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) { 重点
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;重点
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;重点
                        }

 

 

2 jdk8 引入红黑树

3 1.7 先扩容再插入,1.8先插入再决定是否扩容

4 1.7头插(

同一位置上新元素总会被放在链表的头部位置;这样先放在一个索引上的元素终会被放到Entry链的尾部

),1.8尾插

 

细节:为什么是8

1)treenodes的大小大约是常规节点的两倍——决定了不能直接用,有优势也有开销

2)红黑树平均查找长度是log(n),长度为8的时候,平均查找长度为3,如果继续使用链表,平均查找长度为8/2=4,这才有转换为树的必要

3)还有选择6和8,中间有个差值7可以有效防止链表和树频繁转换。

 

细节:为什么0.75 

如果是0.5 , 那么每次达到容量的一半就进行扩容,默认容量是16, 达到8就扩容成32,达到16就扩容成64, 最终使用空间和未使用空间的差值会逐渐增加,空间利用率低下。  如果是1,那意味着每次空间使用完毕才扩容,在一定程度上会增加put时候的时间及hash冲突的可能,提高读写的成本

空间使用率和性能的折中

 

红黑树根据什么排序:

如果key没有实现Comparable接口

        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        ((p = (ParameterizedType)t).getRawType() ==
                         Comparable.class) &&
                        (as = p.getActualTypeArguments()) != null &&
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;

 

        static int tieBreakOrder(Object a, Object b) {
            int d;
            if (a == null || b == null ||
                (d = a.getClass().getName().
                 compareTo(b.getClass().getName())) == 0)
                d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                     -1 : 1);
            return d;
        }

 

先根据类名,再根据identityHashCode

identityHashCode根据对象的内存地址来计算hashcode,不管对象是不是重写了hashcode

 

https://blog.csdn.net/weixin_41725090/article/details/82147576

https://blog.csdn.net/qq_36520235/article/details/82417949

 

posted on 2019-09-06 15:29  silyvin  阅读(405)  评论(0编辑  收藏  举报