谈谈知识的融会贯通:以“java中的迭代器失效问题”为例

提示

文中涉及知识点:

  • Collection 、 Iterator
  • Guava 中的 Lists.partition 方法

如果你对这两个知识点不了解,强烈建议阅读文中引用的参考文章。

场景一:以ArrayList为例

参考文章 java迭代器失效Collection与Iterator的remove()方法区别与ConcurrentModificationException异常 ,可将迭代器和 Collection 的不同理解为:迭代器是基于 Collection 的一个视图,迭代器执行诸如 remove 和 add 之类的操作时,会首先在底层 Collection 上操作,最后将 expectedModCount 更新为新的 modCount ,而直接操作 Collection 则只会更新 modCount ,导致 fail-fast 机制生效。因此我们应在涉及到此类操作时尽可能只使用迭代器,可参考文章 Java:使用Iterator迭代器遍历集合数据

场景二:以Guava中的Lists.partition为例

参考文章 列表分片实现Java 集合细节(三):subList 的缺陷 ,可知 Lists.partition 的底层实现就是 subList 方法,而 subList 函数返回仅仅只是一个视图,因此这里的 subList 其实和前面所述的迭代器在概念上是一样的(只是新的分片 List 的计数变量不叫 expectedModCount,而是和原 List 一样的 modCount,通过 this 区分),它们都是在原 List 的上层产生的视图,然后我们使用这个视图进行各种操作。因此,第二篇文章中所谓的 subList 缺陷其实不能叫做缺陷:我们在原 List 上通过 subList 获得其分片视图后,就不应该再操作原 List 了(类似于迭代器,我们获得一个 List 的迭代器后,应使用该迭代器进行各种操作,如前所述,此时针对原 List 的一些操作是危险的)。

当然,第二篇文章之所以将其称为“缺陷”也有原因:我们通常会认为获得的新分片 List 是新的 List ,这就是语言层面的理解了。

场景三:浅谈Guava中的集合类

其实,Guava中的多种集合如Maps、Lists均没有实现自定义的add操作。以Lists为例,当我们用Lists.transform或是Lists.partition函数返回新的集合时,都是Lists的内部类,这些内部类同样没有实现add方法,因此当我们视图在新的集合上进行add操作时,就会调用AbstractList的add方法,此方法会直接抛出UnsupportedOperationException。不过,我们仍旧可以在旧的集合中进行add操作,此时新集合也能看到刚刚add进去的元素,这是需要注意的。

总结

有时很多概念描述有很大不同,其实内部原理是相通甚至相同的,将这些概念融会贯通将大有裨益。
如果你了解 C++ 的话,可以参考 vector 容器的 insert 和 erase 方法,其实底层原理和 java 也是类似的。

扩展:多线程场景

注意,ArrayList并不是线程安全的,若不追求数据强一致性,可使用 CopyOnWriteArrayList 方法,可参考 聊聊并发-Java中的Copy-On-Write容器JDK 5.0 中更灵活、更具可伸缩性的锁定机制

其他扩展文章

为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作

原文地址:https://www.cnblogs.com/xiaoxi666/p/9715689.html