在遍历数组的时候, 我们一般使用一般的 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 的值没有在发生变化, 所以也就没有在发生并发修改异常.
来源: https://www.cnblogs.com/donghaibin/p/9195767.html