数据 List dataList = new ArrayList();
1、for循环
我们一般可以见到类似如下的遍历方式
for(int i=0;i <dataList.size();i++){
dataList.get(i);
}
这样的遍历一般边遍历边往列表里面加item可以往i以后加,不能往i以前加,否则加入的item就漏遍历了,而且i位置的item会遍历两遍。
比如列表[1,2,3,4] 当i=1的时候,dataList.get(i)是2,插入5便是[1,5,2,3,4],下一次i是2,dataList.get(i)还是2。
删除操作也是能删除i以后,不能删除i及i以前的item。
比如 列表 [1,2,3,4],如果遍历到i=1的时候删除了dataList.get(i)=2,那么列表就变成 [1,3,4],长度为3,i已经等于1了,下一次i加1取列表item,i就是2,dataList.get(i)就是4了。会漏遍历item值为3的item,所以边遍历边删除会有遍历不完整的问题。
所以总结为:for循环遍历,如果要边遍历边删除或者增加,那么只能把数据添加到当前还没有遍历到的位置。
2.Iterator迭代器遍历
Iterator<Integer> iterable= dataList.iterator(); while (iterable.hasNext()){ int a = iterable.next();
}
我们来看一下源码的iterator到底是什么
ArrayList的iterator是
public Iterator<E> iterator() { return new Itr(); }
private class Itr implements Iterator<E> { protected int limit = ArrayList.this.size; 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 < limit; }
public E next() { if (modCount != expectedModCount)//检查迭代器记录的变更次数是否和ArrayList记录的变更次数一样,否则抛异常。也就是说遍历的时候除了迭代器,谁也不能更改列表(调用add或者remove),否则下次调用next就抛异常 throw new ConcurrentModificationException(); int i = cursor; if (i >= limit) 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(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); try {//迭代器自己可以调用remove,调用完会同步更改次数 ArrayList.this.remove(lastRet); cursor = lastRet;//移除后接着遍历移除位置的下一个数据 lastRet = -1; expectedModCount = modCount;//同步更改次数 limit--; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
可以看出 ArrayList用Iterator遍历的话,也只能用Iterator移除才行,调用Iterator移除是安全的,不会漏遍历也不会多遍历。不能添加也不能通过ArrayList对象自己移除或者添加,否则抛出异常。
全局列表多处访问或者多线程访问的时候尤其要注意,用Iterator遍历是很危险的事情,要做好同步锁和添加移除控制。
3.foreach遍历
for (int r : dataList) { }
foreach的底层实现可以参考 肥肥技术宅 大佬的博客(foreach 循环的底层原理及正确使用方式,一定要掌握这些!_foreach底层_肥肥技术宅的博客-CSDN博客)
大概的意思就是 foreach是个语法糖,在编译阶段就会被转译。
数组类型列表遍历会执行
for(int i=0;i <size;i++)
集合类型列表遍历会执行
Iterator<Integer> iterable= dataList.iterator(); while (iterable.hasNext()){ int a = iterable.next();
}
转来转去其实只有两种遍历,foreach只是为了简化代码书写。