ArrayList 源码分析02之iterator()、forEach()遍历注意点

1 遍历迭代器 iterator()

1.1 Iterator< E > iterator()

  • iterator() 返回一个通用的遍历迭代器 new Itr(),只有四个对外的方法
/* 直接返回一个遍历器 */
public Iterator<E> iterator() {
    return new Itr();
}
/* Itr 实现 Iterator 遍历器 */
private class Itr implements Iterator<E> {
    int cursor;       // 要返回的下一个元素的索引,默认为 0
    int lastRet = -1; // 最后一个返回值的下标,初始值为-1即没返回
    int expectedModCount = modCount;
    Itr() {} //空构造,什么都不做
    /* 是否有下一个元素 */
    public boolean hasNext() {
        return cursor != size;
    }
	/* 返回下一个元素 */
    @SuppressWarnings("unchecked")
    public E next() {
    	/* 检查 modCount 有没有被修改 */
        checkForComodification();
        int i = cursor;
        /* 超出界限,报错 NoSuchElementException */
        if (i >= size)
            throw new NoSuchElementException();
        /* 获取 arrayList 的 elementData, cursor 移位 +1 即指针移至下一位,返回 移位前的指针所指的元素 */
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
	/* 移除 */
    public void remove() {
    	/* 没有返回过,报错 IllegalStateException */
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
        	/* 调用 ArrayList remove(int index),前面已经解释过了 */
            ArrayList.this.remove(lastRet);
            /* 因为lastRet 位置的元素被删除,下一个元素会左移,即 cursor = lastRet还是指向原先的下一个元素  */
            cursor = lastRet;
            /* 最后返回的元素被删除,置为 -1 */
            lastRet = -1;
            /* 发生删除操作,期望修改值expectedModCount 更新 */
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

	/* 对遍历剩下的元素进行指定的操作 */
    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> 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();
        }
        /* 从 i 即 [cursor ~ size) 的区间,进行传入的 accept 操作 */
        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();
    }
	/* 检测 modCount 有没有改变,即 arrayList 有没有被外部修改 */
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}
  • 代码测试
import java.util.ArrayList;
import java.util.Iterator;

public class IteratorTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        arrayList.add(40);

        Iterator<Integer> iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            Integer e = iterator.next();
            System.out.println("当前元素: " + e);
            if(e == 10) {
                iterator.remove();
            }
            /* forEachRemaining 也会改变 cursor 指针 */
            if(e == 20) {
                iterator.forEachRemaining((Integer integer) -> {
                    System.out.println("forEachRemaining 后的值: " + integer);
                });
            }
        }
        System.out.println();
        System.out.println("iterator 操作后的 arrayList --> " + arrayList);

    }
}

在这里插入图片描述

1.2 列表遍历迭代器ListIterator< E > listIterator(int index)

  • ListIterator< E> listIterator() 相当于 ListIterator< E> listIterator(0),
public ListIterator<E> listIterator() {
    return new ListItr(0);
}
public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}
  • ListItr extends Itr implements ListIterator 即 ListItr 也是一个 遍历器,只不过扩展了一些功能
private class ListItr extends Itr implements ListIterator<E> {
	/* 构造器,指定了 cursor 指针的下标位置,默认为 0 */
    ListItr(int index) {
        super();
        cursor = index;
    }
    /* 是否有前置元素 */
    public boolean hasPrevious() {
        return cursor != 0;
    }
	/* 返回下一个元素的下标,即 cursor */
    public int nextIndex() {
        return cursor;
    }
	/* 返回上一个元素的下标,即 cursor - 1 */
    public int previousIndex() {
        return cursor - 1;
    }
	/* 返回前置元素,如果存在的话 */
    @SuppressWarnings("unchecked")
    public E previous() {
        checkForComodification();
        int i = cursor - 1;
        if (i < 0)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        /* 指针前移 */
        cursor = i;
        return (E) elementData[lastRet = i];
    }
	/* 设置最后一个返回的元素为 传入的 e */
    public void set(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.set(lastRet, e);
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
	/* 在 cursor 出添加一个元素,调用的是 ArrayList 的 add(int index, E e) */
    public void add(E e) {
        checkForComodification();
        try {
            int i = cursor;
            ArrayList.this.add(i, e);
            cursor = i + 1;
            /* 因为下标变了,lastRet 置为 -1 */
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

  • 代码测试: 注意 previous 会将指针前移,next 则会指针后移,取出来的元素就是操作的元素。
package cn.cerish.container.collection.arrayList;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

public class ListIteratorTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        arrayList.add(40);
        // 相当于 arrayList.listIterator(0)
        ListIterator<Integer> listIterator = arrayList.listIterator();
        System.out.println("刚获得遍历器 listIterator,hasPrevious --> " + listIterator.hasPrevious());
        System.out.println("刚获得遍历器 listIterator,nextIndex --> " + listIterator.nextIndex());
        System.out.println("刚获得遍历器 listIterator,previousIndex --> " + listIterator.previousIndex());
        System.out.println("now arraylist -->" + arrayList);

        System.out.println("================================================");
        Integer integer = listIterator.next();
        System.out.println("获得遍历器的 next 元素: " + integer);
        System.out.println("获得遍历器的 next 元素:hasPrevious --> " + listIterator.hasPrevious());
        System.out.println("获得遍历器的 next 元素:previous --> " + listIterator.previous());
        System.out.println("获得遍历器的 next 元素:nextIndex --> " + listIterator.nextIndex());
        System.out.println("获得遍历器的 next 元素:previousIndex --> " + listIterator.previousIndex());
        System.out.println("now arraylist -->" + arrayList);
        System.out.println("================================================");
        listIterator.set(100);
        System.out.println("listIterator.set():,hasPrevious --> " + listIterator.hasPrevious());
        System.out.println("listIterator.set():,previous --> " + (listIterator.hasPrevious()? listIterator.previous() : -1));
        System.out.println("listIterator.set():,nextIndex --> " + listIterator.nextIndex());
        System.out.println("listIterator.set():,previousIndex --> " + listIterator.previousIndex());
        System.out.println("now arraylist -->" + arrayList);
        System.out.println("================================================");
        listIterator.add(50);
        System.out.println("listIterator.add():,hasPrevious --> " + listIterator.hasPrevious());
        System.out.println("listIterator.add():,previous --> " + (listIterator.hasPrevious()? listIterator.previous() : -1));
        System.out.println("listIterator.add():,nextIndex --> " + listIterator.nextIndex());
        System.out.println("listIterator.add():,previousIndex --> " + listIterator.previousIndex());
        System.out.println("now arraylist -->" + arrayList);
    }
}

在这里插入图片描述

1.3 分割迭代器ArrayListSpliterator< E >

  • 将 ArrayList 分割成多个部分,并对其做一定的操作
static final class ArrayListSpliterator<E> implements Spliterator<E> {
    private final ArrayList<E> list;
    private int index; // 分割起始位置
    private int fence; // 分割终点位置
    private int expectedModCount; // initialized when fence set

    /** 创建指定范围分割 list */
    ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
                         int expectedModCount) {
        this.list = list;
        this.index = origin;
        this.fence = fence;
        this.expectedModCount = expectedModCount;
    }
	/* 获取分隔位置 */
    private int getFence() {
        int hi; // (a specialized variant appears in method forEach)
        ArrayList<E> lst;
        /* 由于默认为 -1(构造器传入),判断是否分割过,首次默认不分割 */
        if ((hi = fence) < 0) {
            if ((lst = list) == null)
                hi = fence = 0;
            else {
                expectedModCount = lst.modCount;
                hi = fence = lst.size;
            }
        }
        return hi;
    }
	/* 尝试分割,并返回分割的前半段(二分法) */
    public ArrayListSpliterator<E> trySplit() {
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        /* lo == mid 即 list 只有一个元素 */
        return (lo >= mid) ? null :
            new ArrayListSpliterator<E>(list, lo, index = mid,
                                        expectedModCount);
    }
	/* 对 index 位置的元素进行指定的操作 */
    public boolean tryAdvance(Consumer<? super E> action) {
        if (action == null)
            throw new NullPointerException();
        int hi = getFence(), i = index;
        /**/
        if (i < hi) {
            index = i + 1;
            @SuppressWarnings("unchecked")
            E e = (E)list.elementData[i];
            action.accept(e);
            if (list.modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return true;
        }
        return false;
    }
	/* 对遍历器的每个元素进行指定操作 */
    public void forEachRemaining(Consumer<? super E> action) {
        int i, hi, mc; // hoist accesses and checks from loop
        ArrayList<E> lst; Object[] a;
        if (action == null)
            throw new NullPointerException();
        if ((lst = list) != null && (a = lst.elementData) != null) {
            if ((hi = fence) < 0) {
                mc = lst.modCount;
                hi = lst.size;
            }
            else
                mc = expectedModCount;
            if ((i = index) >= 0 && (index = hi) <= a.length) {
                for (; i < hi; ++i) {
                    @SuppressWarnings("unchecked") 
                    E e = (E) a[i];
                    action.accept(e);
                }
                if (lst.modCount == mc)
                    return;
            }
        }
        throw new ConcurrentModificationException();
    }
	/* 估算遍历器还有多少个元素 */
    public long estimateSize() {
        return (long) (getFence() - index);
    }
	/* 特征值:ORDERED 有序 | SIZED 使用 estimateSize 作为元素个数 | SUBSIZED trySplit() 子串也必须是SiZED 和 SUBSIZED */
    public int characteristics() {
        return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
    }
}
  • 代码示例
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Spliterator;

public class SplitIteratorTest {
    private static String getFormatStr(int num, int len) {
        String integerMaxValueStr = Integer.toBinaryString(num);
        int a = len;
        StringBuilder sb = new StringBuilder();
        int l = integerMaxValueStr.length();
        int i = 0;
        for (; a > 0; --a) {
            if (--l >= 0) {
                sb.append(integerMaxValueStr.charAt(l));
            } else {
                sb.append("0");
            }
            if (++i % 4 == 0) {
                if (a > 1) {
                    sb.append("-");
                }
                i = 0;
            }
        }
        return sb.reverse().toString();
    }
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        arrayList.add(40);
        System.out.println("当前 arrayList: " + arrayList);
        System.out.println("======================================");
        Spliterator<Integer> spliterator = arrayList.spliterator();
        System.out.println("spliterator 的状态: " + getFormatStr(spliterator.characteristics(),32));
        System.out.println("刚创建完 spliterator,spliterator中还有多少个元素: " + spliterator.estimateSize());
        spliterator.tryAdvance((e) -> System.out.println("当前的元素: " + e));
        System.out.println("执行完tryAdvance(), spliterator中还有多少个元素: " + spliterator.estimateSize());
        System.out.print("我是 foreach: 我是剩下的元素");
        spliterator.forEachRemaining((e) -> {
            System.out.print("\t " + e);
        });
        System.out.println();
        System.out.println("======================================");
        Spliterator<Integer> spliterator01 = spliterator.trySplit();
        System.out.print("我是 foreach: 我来遍历spliterator01 元素: ");
        spliterator.forEachRemaining((e) -> {
            System.out.print("\t " + e);
        });
        System.out.println();
        System.out.println("spliterator 的状态: " + getFormatStr(spliterator.characteristics(),32));
    }
}
  • characteristics 特征是用来描述元素的特征且限制某些行为,具体请看 Spliterator 篇了解。
    在这里插入图片描述

2. foreach()

  • 遍历循环每个元素,执行传入的 action 动作
public void forEach(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    @SuppressWarnings("unchecked")
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    /*  */
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        action.accept(elementData[i]);
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}
  • 测试代码,单纯拿来遍历没问题,但发生删除操作,就出事了,报错 ConcurrentModificationException
import java.util.ArrayList;

public class ForEachTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        arrayList.add(40);
        arrayList.add(50);
        arrayList.add(60);
        System.out.println("============ 遍历 arrayList 的元素 ===========");
        arrayList.forEach((e) -> System.out.print(e + "\t"));
        System.out.println();
        System.out.println("=========== 若发生改动 list 操作 ============");
        /* 若遍历发生删除操作 */
        arrayList.forEach(e -> {
            System.out.println("当前元素为: " + e);
            // arrayList.remove(e);
            arrayList.add(100); // 就算是 add() 操作,也会报错 ConcurrentModificationException
        });
    }
}

在这里插入图片描述

3. replaceAll(UnaryOperator operator)

  • 遍历所有的元素,根据传入的规则,进行整体的替换操作
/* 与 replaceAll() 类似,只不过传入的参数类型不同 */
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) {
    Objects.requireNonNull(operator);
    final int expectedModCount = modCount;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        elementData[i] = operator.apply((E) elementData[i]);
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}
  • 代码测试
import java.util.ArrayList;

public class ReplaceAllTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        System.out.println("============ 遍历 replaceAll() arrayList 的元素 ===========");
        System.out.println("replaceAll() 前的 arrayList: " + arrayList);
        arrayList.replaceAll((e) -> e * 10);
        System.out.println("replaceAll() 后的 arrayList: " + arrayList);

        System.out.println("========= 若replaceAll()过程 发生改动 list 操作 ============");
        arrayList.replaceAll((e) -> {
                    arrayList.add(1000);
                    return e * 10;
                }
        );
    }
}

在这里插入图片描述

4 foreach 与 iterator 的区别

  • 会看 foreach 发生报错的地方,modCount 被修改了,即 remove 发生了 modCount 修改
if (modCount != expectedModCount) {
    throw new ConcurrentModificationException();
}
/* 可以回看remove源码,就是这一段修改了 modCount */
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
}
  • 那问题来了,我想遍历,根据条件删除某一个元素,该怎么办呢?使用 iterator。
import java.util.ArrayList;
import java.util.Iterator;

public class IteratorTest {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(10);
        arrayList.add(20);
        arrayList.add(30);
        arrayList.add(40);
        Iterator<Integer> iterator = arrayList.iterator();
        System.out.println("iterator 遍历前的 arrayList: " + arrayList);
        Iterator<Integer> iterator1 = arrayList.iterator();
        while (iterator1.hasNext()) {
            Integer next = iterator1.next();
            if(next == 20) iterator1.remove();
        }
        System.out.println("iterator 遍历后的 arrayList: " + arrayList);
    }
}

在这里插入图片描述

  • 为什么 iterator.remove() 就可以?
public void remove() {
    if (lastRet < 0)
                throw new IllegalStateException();
    checkForComodification();
    try {
    	/* 这里调用的也是删除 */
        ArrayList.this.remove(lastRet);
        /* 这里是关键,将 cursor 指向被删除的下标,因为删除元素之后,后面元素左移一位,cursor指针也应当前移 */
        cursor = lastRet;
        lastRet = -1;
        /* 这里会修改 expectedModCount */
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}
  • 总结:foreach 循环删除会报错是因为 modCount 被修改,iterator 的 modCount 会修正,但关键的地方还是cursor指针,在修改之后,一直指向原本指向的元素的下标。
  • 步骤解释 iterator.remove()
假装我是一个图:
下标          0   1   2   3   4   5   6   7   8   9     

list     :[  10  20  30  40  50  60  70  80  90  100  ]
iterator :[  10  20  30  40  50  60  70  80  90  100  ] cursor = 0,laetRet = -1
执行一次 iterator.next() 后
list     :[  10  20  30  40  50  60  70  80  90  100  ]
iterator :[  20  30  40  50  60  70  80  90  100  ] cursor = 1,laetRet = 0
继续执行一次 iterator.next() 后
list     :[  10  20  30  40  50  60  70  80  90  100  ]
iterator :[  30  40  50  60  70  80  90  100  ] cursor = 2,laetRet = 1
执行 iterator.remove() 即执行 ArrayList.this.remove(1);
list     :[  10  30  40  50  60  70  80  90  100  ]
iterator :[  30  40  50  60  70  80  90  100  ] 
重点来了,到现在为止,cursor = 2,laetRet = 1, 那么 cursor 指向了40,laetRet 指向了30,这不对啊
所以,cursor = lastRet = 1 指向原来的30,而原来的laetRet 指向被删除的20,应该取消指向,lastRet = -1,修改modCount版本: expectedModCount = modCount;
此时cursor = 1,lastRet = -1,继续执行一次 iterator.next() 后
list     :[  10  30  40  50  60  70  80  90  100  ]
iterator :[  40  50  60  70  80  90  100  ] cursor = 2,laetRet = 1, 又可以愉快的进行游戏了

5. 总结

  • ArrayList 有三种遍历器,通用遍历器的 iterator()、列表遍历器 listIterator()、数组列表分割遍历器 ArrayListSpliterator(),可根据实际情况选择使用
  • foreach 适用于遍历展示输出,replaceAll 适用于整体做同样的操作替换,如 整体元素+100,但如果涉及删除操作,使用 iterator 才是正确的选择。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值