天天看点

容器删除元素后迭代器失效_迭代器模式

迭代器模式(Iterator Pattern) 又称游标(Cursor) 模式,是行为型设计模式之一。

迭代器模式源于对容器的访问,如 Java 中的 List, Map, 数组等,我们对容器内对象的访问必然涉及遍历算法。如果将遍历算法封装在容器中,那么对于容器类来说就承担了过多的功能;而如果不提供遍历方法让使用者自己去实现,又会让容器的内部细节暴露无遗。此时,迭代器模式应运而生,在客户访问类与容器体之间插入一个第三者——迭代器,很好的解决了上述的弊端。

定义

提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部细节。

使用场景

  • 遍历一个容器对象
  • 对外提供一个可遍历的列表

UML

容器删除元素后迭代器失效_迭代器模式
  • Iterator: 迭代器接口,负责定义、访问遍历元素的接口
  • ConcreteIterator: 具体的迭代器类
  • Aggregate: 容器类接口
  • ConcreteAggregate: 具体容器类

几乎所有的高级语言的容器类都为我们提供了相应的迭代器,而开发者也几乎不会自己去实现一个迭代器,我们直接来看下迭代器的用法:

List lst = new ArrayList();
lst.add("aaa");
lst.add("bbb");
lst.add("ccc");
lst.add("ddd");
Iterator iterator = lst.iterator();while(iterator.hasNext()) {
    System.out.println(iterator.next());
}
           

看明白这个 UML 中迭代器的各个角色,接下来直接来看一下源码中的迭代器模式实现。

Android 源码中的实现

Android 源码中,也有了各种数据结构体,如 List, Map 所包含的迭代器外,还有提供迭代器来遍历访问各种数据,最典型的例子就是数据库查询使用的 Cursor。当我们使用 SQLiteDatabase 的 query 方法查询数据库时,会返回一个 Cursor 游标对象,这个 Cursor 的实现就是一个典型的迭代器。

ConcurrentModificationException

由于迭代器本身非常简单,这边说个我们在使用 Iterator 经常会遇到的异常做为文章内容补充。

容器删除元素后迭代器失效_迭代器模式

复现路径:

List list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
Iterator iterator = list.iterator();while (iterator.hasNext()) {
    String s = iterator.next();if (s.equals("bbb")) {
        list.remove(s);
    }
}
           

可能很少人会直接写这样的代码,但是这样呢:

其实这种增强的 for 循环,实际上就是 IDE 在编译出的 class 文件也是使用 Iterator 遍历,因而也会抛异常

容器删除元素后迭代器失效_迭代器模式

原因

在 List 的基类中有这样一个属性

而 List 在生成 Iterator 的时候会把 modCount 存到

java.util.AbstractList.Itr#expectedModCount

里面,遍历时如果操作了 List, 即 modCount 与 Iterator 中的 expectedModCount 对应不上的话,便主动抛出异常了。

解决方案

1. 单线程出线该问题处理比较简单,删除时调用

java.util.Iterator#remove

2. 多线程遇到该问题时,则需要用线程同步,在遍历时锁住 List, 防止被操作。或直接使用 JDK 提供的

java.util.concurrent.CopyOnWriteArrayList

作为容器

小结

优点

  • 支持以不同的方式去遍历一个容器对象
  • 对外界隐藏容器对象的实现细节

缺点

  • 类增加了,麻烦。

另外我们今天还介绍了 ConcurrentModificationException 异常产生的原因和解决方案

推荐阅读:

访问者模式

Android 底层的进程间同步机制

Android事件分发机制抽象--钓钩模型

vsync 信号生成源码分析

年轻人不讲码德,我大意了,没有 Review

介绍一下 Android Handler 中的 epoll 机制?

开源一组视频时间轴控件

容器删除元素后迭代器失效_迭代器模式

关注我

助你升职加薪

Android 面试官

容器删除元素后迭代器失效_迭代器模式
容器删除元素后迭代器失效_迭代器模式

点赞再看,年薪百万