通用迭代器对象:Iterator()
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回元素的下标
int lastRet = -1; // 上一个要被返回元素的下标,如果没有,则为-1
int expectedModCount = modCount;//用来做快速失败检测
//判断是否还有下一个元素
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
//获取下一个元素
public E next() {
checkForComodification();//快速失败检测,检测expectedModCount跟modCount是否相等,
//判断在遍历期间list是否被改变了结构
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;//cursor移向下一个元素
return (E) elementData[lastRet = i]; //将lastRet设置为被返回元素的下标
}
//删除最近一个被next()返回的元素。
public void remove() {
if (lastRet < 0) //由此可知,在调用remove之前必须调用next(),只有next()方法会重置lastRet。
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); //删除元素
cursor = lastRet; //回退一步
lastRet = -1; //lastRet重置为-1,不能多次重复调用remove()
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
//暂时先不管,这个是为Lambda表达式而准备的
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
//快速失败检测
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList的迭代器通过一个实现了Iterator接口的内部类实现。这样很好的隐藏了内部实现细节,由给外部提供了遍历list的方法。而且因为ArrayList不是线程安全的,采用了“快速失败”检测机制来防止多线程环境下:遍历过程中别的线程修改了list结构的问题。对于“快速失败”检测,我们后面再讲。
由此,我在这里说一下遍历一个list的几种方式。
- for循环遍历(ArrayList推荐,它底层是用数组实现,用下标访问最快。但LinkedList还是用iterator比较好)
for(int i=0;i<list.size();i++){
System.out.print(list.get(i)+" ");
}
- 超级for循环遍历(不推荐,虽然实质上是调用迭代器进行循环,但这个转换过程也是需要时间的)
for(Object o : list){
System.out.print(o+" ");
}
- 迭代器遍历(所有collection通用,推荐使用)
Iterator iterator = list.iterator();
while(iterator.hasNext()){
Object o = iterator.next();
System.out.print(o+" ");
}
特定迭代器:listIterator() 和 listIterator(int i)
public ListIterator<E> listIterator() {
return new ListItr(0);
}
//ArrayList扩展的迭代器,能够向前向后遍历,还可以添加、设置数据
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
获取子列表:subList(int fromIndex, int toIndex)
我们通常用substring来分割字符串,ArrayList也提供了一个subList来获取子列表。
1. subList得到的只是主list的一个视图
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);// 范围检查
return new SubList(this, 0, fromIndex, toIndex);
}
subListRangeCheck只是做一些范围检查,重点是 return new SubList(this, 0, fromIndex, toIndex); 它将this传递给了SubList,这表示原始list,记住这点非常重要!
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
//构造函数
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
//设置方法
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
//获取get方法
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
//获取元素数量size
public int size() {
checkForComodification();
return this.size;
}
//添加元素
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
// 删除元素
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
//范围删除
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
}
SubList是ArrayList的子类,也继承了AbstractList,并且实现了RandomAccess接口。也拥有常规list中的添加、删除、设置值等方法。
但是它的构造函数有点特殊,在该构造函数中有两个地方需要注意:
1、this.parent = parent;而parent就是在前面传递过来的list,也就是说this.parent就是原始list的引用。
2、this.offset = offset + fromIndex;this.parentOffset = fromIndex;。同时在构造函数中它甚至将modCount(fail-fast机制)传递过来了: this.modCount = ArrayList.this.modCount 。
我们再看get方法,在get方法中
return ArrayList.this.elementData(offset + index);
这段代码可以清晰表明get所返回就是原列表offset + index位置的元素。同样的道理还有add方法里面的:
parent.add(parentOffset + index, e);
也同样表明SubList的操作实质上是对原始list的操作,都会反映到原始list中。
2. subList生成子列表后,不要试图去操作原列表
SubList中有大量的
checkForComodification();
在add,remove,set,get,size等方法中。都有对checkForComodification()的调用。
而这个方法具体是干什么呢。
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
判断主list的modCount是否与子list的modCount相等。
这都表明在生成子列表之后,如果再去操作主list,并对主list产生了结构性的修改(增加删除元素),那么对子list的所有操作都会抛出ConcurrentModificationException异常。SubList把主ArrayList的”快速失败”机制也一起带过来了。
对于子列表视图,它是动态生成的,生成之后就不要操作原列表了,否则必然都导致视图的不稳定而抛出异常。最好的办法就是将原列表设置为只读状态,要操作就操作子列表:
/通过subList生成一个与list一样的列表 list1
List<Integer> list1 = list1.subList(0, list.size());
//对list设置为只读状态
list = Collections.unmodifiableList(list);
//需要将Collections.unmodifiableList(list);的返回值重新赋值给list,光执行Collections.unmodifiableList(list); 是不起作用的。
3. 推荐使用subList处理局部列表
在开发过程中我们一定会遇到这样一个问题:获取一堆数据后,需要删除某段数据。例如,有一个列表存在1000条记录,我们需要删除100-200位置处的数据,可能我们会这样处理:
for(int i=0;i<list.size();i++){
if(i>=100 || i<=200){
list.remove(i);
//这个代码当然是错误的。当删除第100个元素时,第101个元素会自动往前挪一位,它的索引变成100。
//下一次循环remove(101)实际上删除的是原来102位置上的数据。
//这也是在for循环中不要删除元素的原因。这里只是举个例子说明
}
}
这是大部分人的处理方式。还有一种更简洁有效的方法
list.subList(100,200).clear();
简洁优雅有效!
参考: http://blog.youkuaiyun.com/chenssy/article/details/44102915