JDK22.0.1中的ArrayList中的add

前言:

         随着JDK的发展,里面的一些方法也在不断的更新。今天在学习集合框架时,和老师一起点开ArrayList中的add方法里面的具体的源代码,结果发现我的竟然和老师的不一样。当时我一脸懵,然后认真看了才发现原来老师用的是JDK1.8,而我用的是JDK2.2。哭死,我还特地去比较,原来小丑竟是我自己。

源代码分析:

 废话就不多说了,今天就来盘它。

        当我们在IDEA中选中ArrayList对象的add然后ctrl+B进入源代码就会发现这样的一串代码:

public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }

modCount:

刚开始看我就懵了,这个modCount++是用来干什么的?然后我选中ctrl+B,发现它竟然是在AbstractList类中的,代码如下:

protected transient int modCount = 0;

看到这个transient我又一脸懵,还没开始就被第一行代码卡死在了路上。

然后我就去查资料,发现在这里transient关键字的作用是:使modCount不再能被序列化。

然后身为菜鸡的我,又是一脸懵,序列化?那是什么东东?

随后又去翻越资料,(真的是:路漫漫其修远兮啊)。

Java 序列化:是一种将对象转换为字节流的过程,以便可以将对象保存到磁盘上,将其传输到网络上,或者将其存储在内存中,以后再进行反序列化,将字节流重新转换为对象。序列化使其他代码可以查看或修改,那些不序列化便无法访问的对象实例数据。序列化机制使得对象可以脱离程序的运行而独立存在。

感觉还是有点懵圈吧,那就对了我也是。不过大概意思应该是说:将对象序列化然后再进行传输,也就是相当于将对象解析为了二进制数字去表示然后通过计算机进行传播或者存储。(这个后续会出一篇专门来介绍)

在这里我们对于transient关键字,暂时就到此为止了,往后我会出一篇专门介绍这个关键字和序列化,这个内容太多,在这里就不过多介绍了。

说回正题:

protected transient int modCount = 0;

那这一串代码的意义是什么呢?它用于记录修改次数,快速失败机制的一部分。

快速失败机制???我直接绝了,又是一个神奇的不认识的名称。但是有有点我们是能知道的,就是记录修改的次数。(快速失败机制后续会出一篇专篇)

我们总算是看懂了我们能看懂的,就简而言之,modCount这玩意就是来记录修改次数的。

add(e, elementData, size):

        经过了漫长的查询资料终于也是来到了第二行,解释是:调用内部添加方法将元素添加到数组中。在其中elementDate的定义代码如下:

transient Object[] elementData;

又是transient,不过不影响我们了解这行代码,它用于存储ArrayList元素的数组。

private void add(E e, Object[] elementData, int s):

当c选中add(e, elementData, size)中的add后trl+B跳转到内部添加方法,你就会得到这样一串代码:

private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

这个就偏简单了,也都是我们看得懂的,我直接将我的理解用//放出来

/**
 * 内部添加方法,将元素添加到指定位置
 * @param e 要添加的元素
 * @param elementData 当前存储元素的数组
 * @param s 当前数组中的元素个数
 */
private void add(E e, Object[] elementData, int s) {
    // 如果数组已满,进行扩容
    if (s == elementData.length)
        elementData = grow();
    // 将元素e添加到数组的第s个位置
    elementData[s] = e;
    // 更新size为s+1
    size = s + 1;
}

然后我们选中grow跳转,我们会得到:

private Object[] grow() {
        return grow(size + 1);
    }

这就是一个无参数扩容方法,默认扩容到当前元素个数加一的位置,return扩容后的数组。

这里面的grow()又是内部的哪一个方法,我们继续跳转:

private Object[] grow(int minCapacity):

代码如下:

private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }

这个的话也就是和老师那个大差不差了,作用就是:扩容方法,确保数组能够容纳至少minCapacity个元素。

我也以代码放出我的一个理解:

/**
 * 扩容方法,确保数组能够容纳至少minCapacity个元素
 * @param minCapacity 需要的最小容量
 * @return 扩容后的数组
 */
private Object[] grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // 如果旧容量大于0或者elementData不是默认的空数组
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        // 计算新的容量,考虑最小增长和优先增长
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* 最小增长 */
                oldCapacity >> 1           /* 优先增长 */);
        // 返回扩容后的数组
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        // 返回初始容量为DEFAULT_CAPACITY或minCapacity中的较大值的新数组
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

这里又出现了newLength()方法,继续跳转:

public static int newLength(int oldLength, int minGrowth, int prefGrowth):

跳转之后会有这么一串代码:

public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // preconditions not checked because of inlining
        // assert oldLength >= 0
        // assert minGrowth > 0

        int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
        if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
            return prefLength;
        } else {
            // put code cold in a separate method
            return hugeLength(oldLength, minGrowth);
        }
    }

代码解释如下:

public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
    // newLength方法的主要作用是根据当前数组长度、最小增长量和优先增长量,计算出一个新的数组长度。
    // 它考虑了可能的溢出情况,并确保新长度在合理范围内。

    // 计算理想的数组长度,即当前长度加上最小增长量和优先增长量中的较大值。
    // 使用Math.max(minGrowth, prefGrowth)来确保增长足够大,减少频繁扩容的开销。
    int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // 可能会发生溢出

    // 检查计算出的理想长度是否在合理范围内(大于0且小于或等于SOFT_MAX_ARRAY_LENGTH)。
    if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
        // 如果在合理范围内,则直接返回这个理想长度。
        return prefLength;
    } else {
        // 如果不在合理范围内,则调用hugeLength方法进行进一步处理。
        return hugeLength(oldLength, minGrowth);
    }
}

又出现了hugeLength()方法,我们继续跳转:

private static int hugeLength(int oldLength, int minGrowth):

源代码如下:

private static int hugeLength(int oldLength, int minGrowth) {
        int minLength = oldLength + minGrowth;
        if (minLength < 0) { // overflow
            throw new OutOfMemoryError(
                "Required array length " + oldLength + " + " + minGrowth + " is too large");
        } else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
            return SOFT_MAX_ARRAY_LENGTH;
        } else {
            return minLength;
        }
    }

对代码解析:

private static int hugeLength(int oldLength, int minGrowth) {
    // hugeLength方法用于处理数组长度超出合理范围的情况。
    // 它确保即使在极端情况下(如整数溢出或所需长度过大),也能返回一个适当的数组长度或抛出相应的异常。

    // 计算最小需要的数组长度,即当前长度加上最小增长量。
    int minLength = oldLength + minGrowth;

    // 检查计算出的最小长度是否小于0(可能由于整数溢出导致)。
    if (minLength < 0) { // 溢出
        // 如果小于0,说明发生了整数溢出,抛出OutOfMemoryError异常,表示所需数组长度太大,内存不足。
        throw new OutOfMemoryError(
            "Required array length " + oldLength + " + " + minGrowth + " is too large");
    // 如果最小长度在合理范围内,返回预定义的最大软长度SOFT_MAX_ARRAY_LENGTH。
    } else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
        return SOFT_MAX_ARRAY_LENGTH;
    } else {
        // 如果最小长度超过了合理范围,直接返回这个最小长度。
        return minLength;
    }
}

到这里就结束了,虽然加入了新的玩意但是,简单的去理解还是没什么问题的,就是那些新的名称没有搞明白,到时候我会出专篇去说说transient关键字,序列化,快速失败机制。希望这些能对大家理解ArrayList中add方法的更新有帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值