集合中的 for-Each循环

网友投稿 936 2022-10-24

集合中的 for-Each循环

集合中的 for-Each循环

数组的加强型的for-Each循环很简单,我们再来看一下集合中的for-Each 循环又是怎么样的。我们都知道集合中的遍历都是通过迭代(iterator)完成的。也许有人说,也可以按照下面的方式来遍历集合,不一定非要使用迭代:

1 List list = new LinkedList();2 list.add("a");3 list.add("b");4 list.add("c");5 for(int i=0;i

然而,这种方式对于基于链表实现的List来说,是比较耗性能的,因为get(int i)方法包含了一个循环,而且这个循环就是迭代遍历一次List,直到遇到第i个元素,才停止循环,返回第i个元素。对于数量小,遍历不频繁的List来说,开销可以忽略。否则,开销将不容忽视。

所以,正确集合遍历是使用迭代器Iterator来遍历的:

1 List list = new LinkedList(); 2 list.add("a"); 3 list.add("b"); 4 list.add("c"); 5 //获取集合的迭代器 6 Iterator itor = list.iterator(); 7 //集合的普通for循环 8 for(;itor.hasNext();){//相当于 while(itor.hasNext()) 9 String item = itor.next();10 System.out.println(item);11

再看看对应的for-Each循环的例子:

1 List list = new LinkedList();2 list.add("a");3 list.add("b");4 list.add("c");5 for(String item:list){//for-Each6 System.out.println(item);7

可以看出,for-Each循环比普通for循环要简洁很多。我们依旧回答上面的两个问题:

编译器是如何处理 集合中的for-Each循环的?

1 public static void main(String args[]) 2 { 3 List list = new LinkedList(); 4 list.add("aa"); 5 list.add("bb"); 6 for(String item:list) 7 { 8 if("bb".equals(item)) 9 list.add("cc");10 }11

我们看一下上面例子的 反编译代码

1 public static void main(String args[]) 2 { 3 List list = new LinkedList(); 4 list.add("aa"); 5 list.add("bb"); 6 for(Iterator iterator = list.iterator(); iterator.hasNext();) 7 { 8 String item = (String)iterator.next(); 9 if("bb".equals(item))10 list.add("cc");11 }12

与数组类似,编译器最终也就是将集合中的for-Each循环处理成集合的普通for循环。 而集合的​​Collection​​​接口通过扩展​​Iterable​​​接口来提供​​iterator()​​​方。那么我们换一个角度,是不是只要实现 ​​Iterable​​​接口,提供​​iterator()​​方法,也可以使用 for-Each循环呢?来看个例子:

class MyList implements Iterable{ private ArrayList list = new ArrayList<>(); public void addId(T id){ list.add(id); } public boolean removeId(T id){ return list.remove(id); } @Override public Iterator iterator() {//扩展自Iterable接口 //为了简单起见,就直接使用已有的迭代器 return list.iterator(); } public static void main(String[] args) { MyList myList = new MyList<>(); myList.addId("666999"); myList.addId("973219"); //for-Each for(String item:myList){ System.out.println(item); } }}

上面的例子编译通过,并且运行无误。所以,只要实现了​​Iterable​​接口的类,都可以使用for-Each循环来遍历。

集合迭代的陷阱

集合循环遍历时所使用的迭代器Iterator有一个要求:在迭代的过程中,除了使用迭代器(如:​​Iterator.remove()​​方法)对集合增删元素外,是不允许直接对集合进行增删操作。否则将会抛出 ConcurrentModificationException异常。所以,由于集合的for-Each循环本质上使用的还是Iterator来迭代,因此也要注意这个陷阱。for-Each循环很隐蔽地使用了Iterator,导致程序员很容易忽略掉这个细节,所以一定要注意。看下面的例子,for-Each循环中修改了集合。

public static void main(String[] args) { List list = new LinkedList<>(); list.add("aa"); list.add("bb"); for (String item : list) {//for-Each if ("bb".equals(item)) { list.add("cc"); //直接操作list } } }

运行抛出异常:

2. 集合中的for-Each循环能代替集合的普通for循环吗?

同样也是不能的。集合中的for-Each循环的局限性与数组的for-Each循环是一样的。集合的for-Each循环是不能对集合进行增删操作、也不能获取索引。而集合的普通for循环可以使用的迭代器提供了对集合的增删方法(如:​​Iterator.remove​​​,​​ListIterator.add()​​​),获取索引的方法(如:​​ListIterator.nextIndex()​​​、​​ListIterator.previousIndex()​​);

三、Iterator源码分析

我们来分析一下Iterator源码,主要看看为什么在集合迭代时,修改集合可能会抛出​​ConcurrentModificationException​​异常。以ArrayList中实现的Iterator为例。

先来看一下​​ArrayList.iterator()​​方法,如下:

1 public Iterator iterator() {2 return new Itr();3

​​iterator()​​​方法直接创建了一个类Itr的对象。那就接着看 Itr类的定义吧!发现​​Itr​​​其实是​​ArrayList​​​的内部类,实现了 ​​Iterator​​ 接口。

/** * An optimized version of AbstractList.Itr */ private class Itr implements Iterator { 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(); //ArrayList的底层数组 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 expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer 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(); } }

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:新生命团队基础框架X组件
下一篇:Selenium UI 自动化测试框架(基于python)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~