学习笔记——HashMap总结

优点:查询速度快,理想状态下时间复杂度是O(1)

缺点:空间利用率不高

HashMap是通过数组加链表来实现的。所以一个好的hash算法需要能将key值尽可能地均匀分布到整个数组中。但是再好地hash算法也无法避免

随着数组位置被填充得越来越多,计算后落在数组同一位置的概率会越来越大。所以HashMap通过加载因子控制整个数组的使用率,默认加载因子是0.75,

当整个数组的使用率达到了0.75,就会进行扩容。也就是说,HashMap中至少四分之一的数组空间是被浪费了的。这也就是为什么HashMap的空间利用率不高。

另外HashMap的扩容会带来不小的性能开销,因为需要将原来的key根据扩容后新的数组大小进行rehash。像ArrayList这种可变长数组一样,

如果能预估到所要存放的数据量比较大的话,最好在初始化的时候就直接指定大小。这样可以避免频繁的扩容,导致不必要的性能开销。

这里需要注意下,HashMap初始化的时候,并不是直接初始化成你指定的大小。HashMap中的源码如下:

public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
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;
    }v

实际初始化的大小,是不小于你指定大小的最小的2的n次幂。如指定17,那最终初始化的大小就是2的5次幂32。

再来说下链表,当有多个key被分配到数组的同一个位置上时,就形成了链表。链表的时间复杂度是比较差的O(n),所以要尽可能地避免产生链表。

在JDK1.8中,HashMap引入了红黑树对链表进行了优化。当链表长度达到8的时候,会自动将链表转换成红黑树(时间复杂度是O(logN))。

当树的节点数减少到6的时候,再自动变换成链表。

 

posted @ 2018-12-11 11:08  boogieman  阅读(279)  评论(0编辑  收藏  举报