在看到 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.) 因为没有研究编发编程也没有亲自试验过,所以暂时不写了,以后研究了并发再补上