foreach的真面目
Posted donghaibin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了foreach的真面目相关的知识,希望对你有一定的参考价值。
在遍历数组的时候,我们一般使用一般的for循环、增强的for循环,在遍历集合的时候,我们一般使用一般的for循环、增强的for循环、迭代器,当然除了这些我们还可以使用我们的工具类,Arrays和collections来遍历;但是今天我们的“猪脚”是增强for循环,也就是foreach循环。
我们分别看下foreach在数组和在集合遍历的时候,两者有什么区别:
1.在集合中:
a:先看下源码:
public class Demo { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("张三"); list.add("李四"); list.add("王五"); for (String string : list) { System.out.println(string); } } }
b:再看经过反编译后的.class文件:
public class Demo { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("张三"); list.add("李四"); list.add("王五"); Iterator var3 = list.iterator(); while(var3.hasNext()) { String string = (String)var3.next(); System.out.println(string); } } }
可以看出foreach在遍历集合的时候,使用的是Iterator(迭代器)对元素进行遍历的。
2.在数组中:
a:先看下源码:
public class ArrayDemo { public static void main(String[] args) { int[] arr = { 2, 3, 4, 1, 5 }; for (int array : arr) { System.out.println(array); } } }
b:看下被反编译的字节码文件:
public class ArrayDemo { public static void main(String[] args) { int[] arr = new int[]{2, 3, 4, 1, 5}; int[] var5 = arr; int var4 = arr.length; for(int var3 = 0; var3 < var4; ++var3) { int array = var5[var3]; System.out.println(array); } } }
可以看出此时的遍历类似于一般的for循环。
所以在遍历数组的时候,使用for循环和foreach循环在效率上是基本上差不多的;在遍历集合的时候,foreach比for循环的效率较高的原因。
那么在遍历集合的时候,使用foreach(底层用的也是迭代器)对其进行操作的时候,为啥会发生如下异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)
at java.util.ArrayList$Itr.next(Unknown Source)
at com.atguigu.add.Demo.main(Demo.java:21)
那么将其的字节码文件也进行反编译一下:
public class Demo { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("张三"); list.add("李四"); list.add("王五"); Iterator var3 = list.iterator(); while(var3.hasNext()) { String string = (String)var3.next(); list.remove(string); } } }
我们看下源码的流程:
/** * 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() {} 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]; }
进入:checkForComodification();
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
删除:
/** * Removes the first occurrence of the specified element from this list, * if it is present. If the list does not contain the element, it is * unchanged. More formally, removes the element with the lowest index * <tt>i</tt> such that * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> * (if such an element exists). Returns <tt>true</tt> if this list * contained the specified element (or equivalently, if this list * changed as a result of the call). * * @param o element to be removed from this list, if present * @return <tt>true</tt> if this list contained the specified element */ public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; } /* * Private remove method that skips bounds checking and does not * return the value removed. */ private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
说明一下过程:
a:当var3.hasNext()的时候,底层返回的是 cursor != size;即是下一个元素的索引 != 元素的实际数量,如果等于的话,那么就返回false,也就说明没有下一个了;反之则返回false。
b:当删除第一个元素的时候,并没有报并发修改异常,而是 modCount++,即是modCount增加了1。
c:由于开始的时候,int expectedModCount = modCount;所以此时 int expectedModCount != modCount;
d:所以在 checkForComodification();报出并发修改异常:throw new ConcurrentModificationException();
那么为啥使用迭代器却不会发生并发修改异常哪,有个同学或许已经发现了这个原因,迭代器使用的是它自己都有的删除元素的方法。
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
此时 modCount的值没有在发生变化,所以也就没有在发生并发修改异常。
以上是关于foreach的真面目的主要内容,如果未能解决你的问题,请参考以下文章