arraylist源码分析

本文深入探讨了ArrayList容器的迭代器模式,解释了为何在遍历过程中直接修改列表会导致并发修改异常,并提供了正确做法。此外,还对比了Iterator与ListIterator的不同之处。

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


在看到 Arrylist容器 api 的时候发现了几个有趣的问题,温故而知新古人不欺我也。以前也看过list的api,但是有的方法没用到过甚至有的时候可以用到没想起来也就忘了,现在又看了一遍,并且结合源码来看真的收获丰多。

首先看一段代码,大家看看这段代码,下面代码的功能是获得一个list,然后删除list中数据为a的数据,当他执行的时候会不会有问题呢?
List<String> list = new ArrayList<String>();
for (int i = 0; i < 5; i++) {
list.add("a" + i);
}
Iterator iterator=list.iterator();
String str = "a0";
while (iterator.hasNext()){
String path=(String) iterator.next();
if (path.contains(str)) {
list.remove(path);
}
}

这段代码在执行的过程中会爆错,具体的报错地方时iterator.next();
1为什么要中这种模式? 这种操纵数据的方式叫做迭代器模式,目的是不公开容器中具体的数据于数据类型,通过迭代器来操作数据。
如果我们不用这种方式的话,就要
for(int i=0;i<list.size();i++){
String a11=list.get(i);
}
通过这种方式暴露了list中封装的数据,岂不是可以直接操作对象的数据?这与面向对象编程中的封装不符合。而用迭代器模式可以避免这种问题。
2 为什么报错? 说一千道一万也不如直接看源码来的容易。
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
*
* @return an iterator over the elements in this list in proper sequence
*/
public Iterator<E> iterator() {
return new Itr();
}



/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

public boolean hasNext() {
return cursor != size;
}

@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}

public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}


public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}


这里是是因为checkForComodification()方法抛出的异常,具体原因大家自行看代码就可以理解了,大体上这样子的,list在创建之初会维护一个记述变量,这个变量modCount是记录的每次新增 删除 或者修改的次数。而在 new Itr();
中会吧modCount变量复制一份到expectedModCount中,大家看好了这是两个变量modCount 和 expectedModCount
初始化的时候是一样子的。当执行list.remove的时候删除了list中的数据,同时modCount加1 但是expectedModCount是iterator中的变量,没有做修改,当执行checkForComodification的时候 里面的逻辑是判断 if (modCount != expectedModCount)
throw new ConcurrentModificationException();
当不想等的时候就会抛出异常,这么做的目的是防止在迭代的过程中 对list容器做操作。我在想api为什么这么设计呢?
api的注释翻译是-
如果列表迭代器结构改性后创建的任何时间,以任何方式除了通过迭代器的删除或添加方法,迭代器将抛出一个concurrentmodificationexception。因此,在面对并发修改时,迭代器会迅速而干净地失败,而不是在未来一个不确定的时间内冒任意的、不确定的行为.。
(英文不好大家理解就好)。
意思就是保证数据的一致性。
那么想达到这种效果 怎么写呢?
下面是正确的代码
吧 list.remove(path); 替换成iterator.remove();
因为iterator.remove()方法中同步了 上面两个变量的值。
3 listIterator() 和 Iterator()的区别? 在这次温习的过程中发现了 listIterator()这个方法,在翻看了api与源码后 发现 listIterator() 比 iterator() 多了几个功能
一.相同点
都是迭代器,当需要对集合中元素进行遍历不需要干涉其遍历过程时,这两种迭代器都可以使用。
二.不同点
(1).使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。
(2).ListIterator有add方法,可以向List中添加对象,而Iterator不能。
(3).ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator不可以。
(4).ListIterator可以定位当前索引的位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
(5).都可实现删除操作,但是ListIterator可以实现对象的修改,set()方法可以实现。Iterator仅能遍历,不能修改。


4 线程不安全?api中的注释说他是不安全的 except that it is unsynchronized.) 因为没有研究编发编程也没有亲自试验过,所以暂时不写了,以后研究了并发再补上





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值