ArrayList 常用遍历方法
1、for 循环遍历
2、增强 for 循环遍历
3、迭代器遍历
迭代器主要用来遍历集合,增强 for 循环底层实现也是迭代器,ListIterator 是更强大的的 Iterator 的子类型
1、只适合于 List 集合的遍历
2、它可以前后双向移动(Iterator 只能向后)
3、可以使用 set 方法替换访问过的元素
4、可以使用 listIterator(n) 创建一个一开始就指向索引为 n 的迭代器
public static void practice5() {
ArrayList<String> practiceStr = new ArrayList<>();
Collections.addAll(practiceStr, "apple", "cat", "dog", "big");
log.info("for 循环遍历");
for(int i= 0;i<practiceStr.size();i++){
log.info(practiceStr.get(i));
}
log.info("增强 for 循环遍历");
for(String element : practiceStr){
log.info(element);
}
log.info("迭代器遍历");
Iterator<String> iterator = practiceStr.iterator();
// List 接口实现了 迭代器接口
// 迭代器接口作为方法的返回值时,内部其实返回了一个 接口的实现类
// Iterator<E> 泛型接口 作为返回值,内部实际返回的是其实现类 new Itr()
// public Iterator<E> iterator() {
// return new ArrayList.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;
//
// Itr() {}
// 。。。
// }
while (iterator.hasNext()){
log.info(iterator.next());
}
}
log.info("list迭代器遍历");
ListIterator<String> iteratorList = practiceStr.listIterator();
while (iteratorList.hasNext()){
log.info("\n");
log.info("{}",iteratorList.nextIndex());
log.info(iteratorList.next());
log.info("{}",iteratorList.nextIndex());
log.info("{}",iteratorList.previousIndex());
}
ListIterator<String> iteratorList1 = practiceStr.listIterator(3);
while (iteratorLis1t.hasNext()){
log.info("\n");
log.info("{}",iteratorList1.nextIndex());
log.info(iteratorList1.next());
log.info("{}",iteratorList1.nextIndex());
log.info("{}",iteratorList1.previousIndex());
}
Iterator接口的定义
public interface Iterator<E> {
boolean hasNext();
E next();
//下面defalut修饰符,java8新增,用于修饰在接口中已经被具体实现方法
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
重点一: remove() 方法。集合自身也有remove()方法,需要注意的是使用迭代器遍历集合时,只能通过迭代器进行add 和 remove。不支持集合本身进行 add 和 remove。查看ArrayList.java源码发现,其中实现的迭代器接口中的 next 方法中会判断集合修改次数,大于 0 时就报错 ConcurrentModificationException,这个异常本身设置的应该是并发修改异常
至于为什么有这样一个原则,是因为避免以下情况出现:
迭代器生成后,迭代器对应的集合项的位置都确定了。迭代过程中当迭代器准备给出下一项时,但该项却被Collection接口的remove方法删除,或者有一个新的项通过Collection接口的add方法插入到正在遍历项的前面,只要上述情况出现,就可能导致迭代器遍历出现问题。
迭代器调用它自己的remove方法是合法的,因为它只能删除刚才已经迭代过的对象。
while (iterator.hasNext()){
String curElement = iterator.next();
log.info(curElement);
if(curElement.equals("cat")){
iterator.remove();
}
}
// 使用迭代器的remove正常
while (iterator.hasNext()){
String curElement = iterator.next();
log.info(curElement);
if(curElement.equals("apple")){
practiceStr.remove("apple");
}
}
// 使用集合的remove报错:java.util.ConcurrentModificationException
ArrayList 迭代器接口中的 next 方法源码
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
protected transient int modCount = 0;
int expectedModCount = modCount;
重点二:一次hasNext()后使用了多次next()方法 TODO
每次调用next()方法之前必须要调用hastNext()方法进行检测;
如果没有调用并且没有下一个元素,直接调用next()方法会抛出 NoSuchElementException异常。
同理如果调用了一次 hasNext() 方法,但是调用了多次next()方法,依旧会抛出异常。