java集合框架List之ArrayList(一)

本文详细介绍了Java集合框架中的ArrayList,包括其内部实现原理、操作示例及线程安全性问题。此外还探讨了modCount字段的作用及其如何帮助实现快速失败机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java集合框架List之ArrayList

list作为我们日常工作中常用的一种集合容器,有必要对这进行一个详细的了解。一个有序的Collection(也称序列),元素可以重复。确切的讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和 e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。实现List的有:ArrayList、LinkedList、Vector等,下面我们先对其中之一的ArrayList进行介绍。

ArrayList的继承结构

这里写图片描述
ArrayList是基于动态数组的数据结构实现的,既然是基于数组的结构,那么它对于随机访问访问的set和get的速度是很快的,时间复杂度为O(1),同样的,对于删除和增加操作就会慢很多了,所以,我们需要根据自己的应用场景来灵活使用。

ArrayList操作示例

List<Integer> list = new ArrayList<>();
// 初始化添加
list.add(1);
list.add(2);
list.add(3);

// 使用Iterator遍历
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
    System.out.println(iterator.next().toString());
}

// 使用循环遍历
for (Integer i : list){
     System.out.println(i.toString());
}

// 移除
list.remove(0);
Iterator<Integer> iterator2 = list.iterator();
while (iterator2.hasNext()){
     System.out.println(iterator2.next().toString());
}

ArrayList是非线程安全的,也就是说它对于并发操作很容易出现脏读或脏写,我们看一下ArrayList读写操作的内部实现

// 增加一个元素
public boolean add(E e) {
   ensureCapacityInternal(size + 1);  
   elementData[size++] = e;
   return true;
}

// 删除一个元素
public E remove(int index) {
   rangeCheck(index);
   modCount++;
   E oldValue = elementData(index);
   int numMoved = size - index - 1;
   if (numMoved > 0){
       System.arraycopy(elementData, index+1, elementData, index,numMoved);
   }
   elementData[--size] = null; 
   return oldValue;
}

可以看到,在无任何锁保护的前提下,对一个位置进行插入时,两个线程对同一个ArrayList拿到的size是一样大的,那么最先写入那个位置的值会被后面的写入所覆盖;删除操作也是同样的,A线程已经将数据删除,B数据再去删除位置,很容易抛出ArrayIndexOutOfBoundsException,所以对于无法保证线程安全的场景下,我们不应该选用ArrayList作为我们的容器选择,那么应该选用哪一个呢?了解了其它的集合容器后我们就知道啦。

有趣的modCount

ArrayList的modCount是从类 java.util.AbstractList 继承的字段

protected transient int modCount

是指从结构上修改此列表的次数。从结构上修改是指更改列表的大小,或者打乱列表,从而使正在进行的迭代产生错误的结果。
此字段由 iterator 和 listIterator 方法返回的迭代器和列表迭代器实现使用。如果意外更改了此字段中的值,则迭代器(或列表迭代器)将抛出 ConcurrentModificationException 来响应 next、remove、previous、set 或 add 操作。在迭代期间面临并发修改时,它提供了快速失败行为,而不是非确定性行为。

子类是否使用此字段是可选的。如果子类希望提供快速失败迭代器(和列表迭代器),则它只需在其 add(int, E) 和 remove(int) 方法(以及它所重写的、导致列表结构上修改的任何其他方法)中增加此字段。对 add(int, E) 或 remove(int) 的单个调用向此字段添加的数量不得超过 1,否则迭代器(和列表迭代器)将抛出虚假的 ConcurrentModificationExceptions。如果某个实现不希望提供快速失败迭代器,则可以忽略此字段。
示例如下

List<Integer> list = new ArrayList<>();
    // 初始化添加
    list.add(1);
    list.add(2);
    list.add(3);

    // 使用Iterator遍历
    Iterator<Integer> iterator = list.iterator();
    while (iterator.hasNext()){
        System.out.println(iterator.next().toString());
        list.add(4);//在迭代过程中对list进行操作
}

在迭代过程中对list进行操作会导致modCount与迭代器内的modCount不一致而快速失败,抛出ConcurrentModificationExceptions异常

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
1
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at com.nfsq.tzs.base.Test.main(Test.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

对于这种情况我们应该使用迭代器自己的add或者remove等来保证modCount的一致性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值