本文共 2848 字,大约阅读时间需要 9 分钟。
在分析以上两种用例时,可以发现主要问题集中在间接结构的modCount变量维护以及迭代器中modCount检查实现的细节。以下是详细的分析过程:
在第一个例子中,代码段如下:
public class TestLinkedList { public static void main(String[] args) { LinkedList linkedList = new LinkedList(); linkedList.add(11); linkedList.add(22); linkedList.add(33); Iterator iterator = linkedList.iterator(); while (iterator.hasNext()) { linkedList.add(10); // 数据结构发生改变 System.out.println(iterator.next()); } System.out.println(linkedList); }}
在这个过程中,迭代器iterator
被获取后,每次循环先执行linkedList.add(10)
,这会修改数据结构,导致modCount增加。在接下来的iterator.next()
中,迭代器发现内部的modCount与自己保存的expectedModCount
不一致,从而抛出ConcurrentModificationException
。
在第二个例子中,代码段如下:
public class TestLinkedList { public static void main(String[] args) { LinkedList linkedList = new LinkedList(); linkedList.add(11); linkedList.add(22); linkedList.add(33); ListIterator iterator = linkedList.listIterator(); while (iterator.hasNext()) { iterator.add(10); // 数据结构发生改变 System.out.println(iterator.next()); } System.out.println(linkedList); }}
在这个递增的过程中,虽然数据结构被修改,但迭代器没有抛出异常。这可能是因为ListIterator
的实现方式在处理modCount时具有不同机制,或者在迭代过程中导致modCount的增加与检查机制不同。
modCount的使用:
LinkedList
维护一个modCount
变量,记录对集合进行操作次数。modCount
值(保存在expectedModCount
变量中)。hasNext
或next
方法被调用时,内部会检查自身的modCount
是否等于当前LinkedList
的modCount
。如果不等,抛出ConcurrentModificationException
。迭代器的生命周期:
expectedModCount
被设为当前集合的modCount
。linkedList
)被修改时,modCount
会被增加。expectedModCount
不再等于当前集合的modCount
时,表示数据结构被修改,抛出异常。默认实现与自定义实现的差异:
Iterator
和ListIterator
,两者的内部分析和modCount检查方式不同。ListIterator
通常提供的是弱一致性迭代器,而Iterator
则要求强一致性。这可能导致在同样的数据操作下,是否会抛出异常的不同。举例如中的问题“为什么会抛出异常”:
测试用例中的修改行为:
add(10)
,导致数据结构变化,从而modCount增加。next()
操作中进行modCount检查,发现在获取时modCount已经发生了变化,符合预期,所以抛出异常。ListIterator
在进行add(10)
时是否会抛出异常,具体取决于ListIterator
的实现。一般来说,ListIterator
的默认实现是支持检测到数据结构修改并抛出异常的。思想与表现的区别:
Iterator
)在结构修改发生时就抛异常。ListIterator
)则允许在某些修改操作下继续使用,直到下一次操作时才可能抛出异常。实际应用中的做法:
ListIterator
允许插入操作,但这会影响modCount的变化检测,导致更多封闭条件。理解迭代器的类型:
检查集合的modCount:
hasNext
或next
时,总是比较当前modCount并抛出异常当有变化。遵守封闭封装原则:
方式探究:
在上述代码中,第一个例子使用迭代器在遍历过程中对数据结构进行了修改,导致modCount
发生变化,进而抛出并发异常。这是通过强一致性迭代器设计的一种防御机制。而第二个例子使用弱一致性迭代器,允许在添加操作后继续使用,并在后续检测到数据结构变化时才抛出异常。这表明在实际应用中,选择合适的迭代器类型对性能和正确性至关重要。
转载地址:http://ckwfk.baihongyu.com/