首页>>后端>>java->JDK8

JDK8

时间:2023-12-07 本站 点击:0

new

JDK1.7:数组+链表  JDK1.8:hash表=数组+链表+红黑树 哇瑟,一个比一个背的熟,面试时稍微问一下就懵逼。

HashMap提供了4个构造函数:

无参构造新建时会给负载因子设置默认为0.75,容量大小默认为16,阈值为0;

这里容量大小默认16只是我们约定的而已,没有添加元素前我们底层的Node数组的length一直为0,一切操作都在put操作之后才展开。

有参构造,使用参数提供的负载因子,容量会由tableSizeFor方法计算出大于等于参数值的一个二次方的数值;阈值初始值会使用容量的值,然后在put元素的时候才会使用公式:容量*负载因子 重新赋值;

tableSizeFor方法具体实现:

我们可以通过反射来查看HashMap在初始化和put后容量阈值的变化,代码如下:

    private  static void test() throws Exception {        //指定初始容量15来创建一个HashMap        HashMap m = new HashMap(16);        //获取HashMap整个类        Class<?> mapType = m.getClass();        //获取指定属性,也可以调用getDeclaredFields()方法获取属性数组        Field threshold =  mapType.getDeclaredField("threshold");        //将目标属性设置为可以访问        threshold.setAccessible(true);        //获取指定方法,因为HashMap没有容量这个属性,但是capacity方法会返回容量值        Method capacity = mapType.getDeclaredMethod("capacity");        //设置目标方法为可访问        capacity.setAccessible(true);        //打印刚初始化的HashMap的容量、阈值和元素数量        System.out.println("初始数据 - 容量:"+capacity.invoke(m)+"    阈值:"+threshold.get(m)+"    元素数量:"+m.size());        for (int i = 0;i<25;i++){            m.put(i,i);            //动态监测HashMap的容量、阈值和元素数量            System.out.println("容量:"+capacity.invoke(m)+"    阈值:"+threshold.get(m)+"    元素数量:"+m.size());        }    }

输出结果如下:

put

接下来我们看put方法:并没有直接使用key的hashcode方法来生成哈希值,而是执行了这个操作: (h = key.hashCode()) ^ (h >>> 16) ;

在执行hashcode方法后再异或 这个右移16位的值,得到了一个新的哈希值,但是哈希冲突仍然不能避免。

接着看putVal方法:

如果node数组为空,即table为空,会执行resize方法返回一个Node<K,V>[] 赋值给tab变量,也就是在这个方法中会给阈值重新计算

resize()方法主要是用来扩容的,下面链表尾插入的时候也会用到;

如果tab对应下标没有值,则新建一个Node放入数组;

这里的(p = tab[i = (n - 1) & hash]) 就体现出我们要求数组长度是2的幂次方的重要性,只有n为2的幂次方,n-1 和hash值与运算才能得到0到n-1的下标值。

如果对应下标有值,hash相同key相同,则会在后面根据一个boolean值判断只够进行覆盖;

为什么会有boolean值,put操作默认为true会执行进行覆盖,但还有一个方法map.putIfAbsent(),也是调用的putVal方法;

另外一点就是put方法和putIfAbsent方法都是有返回值的,返回的都是执行插入操作前key对应的value值。

如果Node是TreeNode类型,即Node数组对应下标中是一个红黑树,将要操作红黑树插入;

否则就是链表的尾插入,即Node数组对应下标中是一个链表;

JDK1.7是头插入 ,1.8是尾插,使用头插会改变链表上的顺序,采用尾部插入能保持链表原本的顺序,jdk1.7的同步插入在扩容时会造成错误,链表中的指向会发生错乱出现循环引用 。

链表插入会循环判断node.next是否为null,链表长度大于8时转成红黑树,因为先判断在插入所有链表会有9个元素,会调用treeifyBin().

treeifyBin方法会先判断Node数组是否大于64,只有大于64才会转为红黑树,不然会调用resize()扩容 一个长链表转为两个短链表

原文:https://juejin.cn/post/7103785290619158536


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/18679.html