浅看ArrayList源码

目录

介绍

0)成员变量

问题1:为什么使用transient

问题2:为什么有时候使用size,有时候使用elementData.length?(如下所示)

问题3:方法中偶尔出现的modCount是什么?

1)构建方法

无参构建:

int容量参数构建:

Collection实现类构建:

2)扩容Add方法

3)remove方法

4)clone克隆方法

5)clear清空方法

6)trimToSize调整容量

7)contains方法

8)forEach方法


介绍

  ArrayList类又称动态数组,同时实现了Collection和List接口,其内部数据结构由数组实现,因此可对容器内元素实现快速随机访问。但因为ArrayList中插入或删除一个元素需要移动其他元素,所以不适合在插入和删除操作频繁的场景下使用。

  ArrayList的容量可以随着元素的增加而自动增加,因此不用担心ArrayList容量不足的问题。(笑)

  ArrayList是非线程安全的。

0)成员变量

第一个变量:扩容时后最小的大小。

第二和第三个变量为空数组,分别在构造函数参数为0和参数为空是设置。

 elementData存储具体数据。

size记录实际存储元素数量。

问题1:为什么使用transient

问题2:为什么有时候使用size,有时候使用elementData.length?(如下所示)

 

问题3:方法中偶尔出现的modCount是什么?

翻译如下:

此列表在结构上被修改的次数。结构修改是指更改列表大小或以其他方式干扰列表,使正在进行的迭代可能会产生不正确结果的修改。此字段由迭代器和列表迭代器方法返回的列表迭代器实现使用。如果此字段的值发生意外更改,迭代器(或列表迭代器)将抛出 ConcurrentModificationException 以响应下一个、删除、上一个、设置或添加操作。这提供了快速故障行为,而不是在迭代期间面对并发修改时的非确定性行为。子类使用此字段是可选的。如果子类希望提供快速失败迭代器(并列出迭代器),那么它只需要在其add(int,E)和remove(int)方法(以及它覆盖的任何其他方法中增加此字段,从而导致对列表的结构修改)。对 add(int,E)或 remove(int) 的单个调用必须向此字段添加不超过一个,否则迭代器(和列表迭代器)将抛出虚假的ConcurrentModificationExceptions。如果实现不希望提供故障快速迭代器,则可以忽略此字段。

1)构建方法

无参构建:

int容量参数构建:

Collection实现类构建:

2)扩容Add方法

先判断是否需要扩容,再插入数据。

 可以看出,在无参构造下,第一次扩容最少扩容至10大小。

看出扩容最小为=原长度+原长度右移一位长度,其余部分为最大容量的检测部分(是否需要报错异常超出最大容量OutOfMemoryError)

 溢出,数据最高位符号位为1,变为负数。

3)remove方法

 可以看出,仅是使用System.arraycopy方法处理,不会进行缩容操作。使最后数组一位为空,去除引用关系,便于GC清除。

4)clone克隆方法

 追踪可知,最终调用native方法:System.arraycopy。

5)clear清空方法

 不会创建一个空对象数组。也不缩容。

6)trimToSize调整容量

 缩容,减少占用空间大小。

在JSONParser序列化中有使用到。

7)contains方法

 可以查询null对象。

使用equals比较对象。

返回第一个匹配的下标。

8)forEach方法

 传递操作类,实现其accept方法进行具体操作。

注意,尽量不要用该方法修改elementData数据,除非了解到数据是否可以修改(!???)

例(修改final对象,X):

    @Test
    public void test11() {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(6);
        list.add(11);
        list.add(21);
        list.forEach(e -> e = e + 1);
​
        System.out.println(list);
    }
​
out---------------Integer是final类!!----------
[1, 6, 11, 21]

例(stream修改法,或者老老实实for循环):

ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(6);
list.add(11);
list.add(21);
​
List<Integer> collect = list.stream().map(e -> e = e + 1).collect(Collectors.toList());
System.out.println(collect);
​
out----------------------------------
[2, 7, 12, 22]

例(修改对象中内容,可):

public class FinallyTest {
​
    public int a = 0;
​
    FinallyTest(int a) {
        this.a = a;
    }
​
    FinallyTest() {
        this.a = 0;
    }
​
    @Override
    public String toString() {
        return "FinallyTest{" +
                "a=" + a +
                '}';
    }
​
}
​
​
class TestT {
​
    @Test
    public void test11() {
        ArrayList<FinallyTest> list2 = new ArrayList<>();
        list2.add(new FinallyTest(1));
        list2.add(new FinallyTest(6));
        list2.add(new FinallyTest(11));
        list2.add(new FinallyTest(21));
        list2.forEach(e -> e.a += 10);
​
        System.out.println(list2);
    }
}
​
​
out--------------------------------
[FinallyTest{a=11}, FinallyTest{a=16}, FinallyTest{a=21}, FinallyTest{a=31}]

总结:

要不是没有女朋友,谁写这个???(5/20日写)

参考:

浅谈 ArrayList 及其扩容机制 - 城北有个混子 - 博客园 (cnblogs.com)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值