HashMap理解

hashmap在jdk1.7和1.8上是有区别的,在1.7上是数组+链表的形式,在1.8上是数组+链表+红黑树的形式。

在讲解hashmap之前我们先讲解一下hash。hash算法就是散列算法。就是把任意长度的输入变成有限长度的输出。是不可逆的算法,像md5,SHA1就是。通常散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,这就是hash碰撞。一个好的hash算法是减小hash碰撞率的。

红黑树,就是一种自平衡的二叉查找树。有如下性质:

性质1. 节点是红色或黑色。

性质2. 根节点是黑色。

性质3 每个叶节点(NIL节点,空节点)是黑色的。

性质4 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。


接下来我们来看hashmap,在1.7上使用了entry数组来存储数据,用hash来得出index,用于存放在数据的位置,如果index相同,会存到同一个位置,这个位置用链表来保存,这就是hashmap解决碰见的链地址法,其他还有开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,如果得出的index都是一样的话,时间复杂度会变大o(n),红黑树我们大致了解了,他的好处就是他的自平衡性,让他这棵树的最大高度为2log(n+1),n是树的节点数。那么红黑树的复杂度就只有 O(log n)。

接下来我们来看一下1.8的源码,在putval方法里,先进行hash算出index,然后判断index的位置是否有值,没有就直接插入进去,如果有,就进行链表的尾插法,如果链表长度达到8就会自动扩容把链表转成红黑树的数据结构来把时间复杂度从O(n)变成O(logN)提高了效率,如果有旧值,就返回旧值,然后拿新值替换旧值存储。

final void treeifyBin(Node[] tab, int hash) {

        int n, index; Node e;

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

            resize();

      //开始树形化

        else if ((e = tab[index = (n - 1) & hash]) != null) {

            TreeNode hd = null, tl = null;

          //对桶Node中的链表元素进行循环,从链表的头节点开始将链表的头元素改为树的头节点

            do {

                TreeNode p = replacementTreeNode(e, null);

                if (tl == null)

                    hd = p;

                else {

                  //树的头节点不为空时

                    p.prev = tl;

                    tl.next = p;

                }

                tl = p;

            } while ((e = e.next) != null);

            //将桶中的元素与树的头节点进行连接

            if ((tab[index] = hd) != null)

                hd.treeify(tab);

        }

    }


hashmap数组的容量默认16,是2的n次方,因为当长度是2的n次方时候,比如1111 11111 都能保证得到和原来hash低位相同,也就是扩容后只有一个位差异,多出来最左边位的1 这样只要对应左边的哪一个差异位为0,就能保证得到新的数组索引和老的数组索引一直 ,这样数据在数组的分布比较均匀,也就是说减少碰撞的几率,减少了链表的遍历,提升效率。

hashmap的扩容,默认的负载因子是0.75,默认的容量是16,如果超过这个值就会进行一次扩容,这个时候需要对所有值都重新计算一次,非常影响性能,所以如果你可以预知长度的话尽量提前设置长度提高性能。一次扩容就是把容量扩大一倍。

hashmap是线程不安全的,要想线程安全需要使用:

方法一:通过Collections.synchronizedMap()返回一个新的Map,这个新的map就是线程安全的. 这个要求大家习惯基于接口编程,因为返回的并不是HashMap,而是一个Map的实现

方法二:重新改写了HashMap,具体的可以查看java.util.concurrent.ConcurrentHashMap. 这个方法比方法一有了很大的改进

方法三:hashtable.

你可能感兴趣的:(HashMap理解)