迭代结束时HashSet的条目给出ConcurrentModificationException

时间:2014-07-22 02:31:05

标签: java multithreading exception generics collections

我在测试使用hashset的类时遇到问题,当我遍历这些元素时,我得到ConcurrentModificationException,尽管我已经知道了(单线程应用程序),任何时候只有一个线程可以访问该类。当两个相同的条目添加到列表中时,这会中断。

private final HashSet<?> entries = new HashSet<>(10);
/**
 * Updates an existing entry if it exists, if not, adds it to the library.
 *
 * @param <T> The type to add
 * @param object The object to test for existence of and to update to
 * @param key The class of the object
 */
public <T> void add(T object, Class<T> key) {
    this.entries.stream().filter((entry) -> (object.equals(entry.getStorage()))).forEach(this.entries::remove);
    this.entries.add(new ClanLibraryEntry<>(object, key));
}

3 个答案:

答案 0 :(得分:5)

请注意HashSet的javadoc:

  

此类的迭代器方法返回的迭代器是快速失败的:   如果在创建迭代器后的任何时间修改了该集,则   除了通过迭代器自己的删除方法,Iterator之外的任何方式   抛出ConcurrentModificationException。

这实际上不需要多个线程。它只需要通过Iterator本身以外的其他东西修改集合。

答案 1 :(得分:1)

您正在使用流,因此此代码违反了针对 non-interference 的一般流原则。请参阅“不干扰”部分中的java.util.stream包文档。这实际上是其他人引用的迭代器的快速失效属性的概括。流实现不一定使用源集合的迭代器,因此该规则不直接应用;但是,概念是相同的:在流运行时,不得修改流的源。如果你很幸运(因为这告诉你代码做错了)或者你不幸运,不一致或不正确的结果,罚款可能是ConcurrentModificationException

看起来意图只是删除某些匹配元素。 Collection接口有一个默认方法removeIf,给定一个谓词,就是这样做的。您可以使用此而不是流。这是代码:

this.entries.removeIf(entry -> object.equals(entry.getStorage()));

答案 2 :(得分:0)

以下内容来自HashSet手册(http://docs.oracle.com/javase/7/docs/api/java/util/HashSet.html):

此类的迭代器方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时间修改集合,除了通过迭代器自己的删除方法之外,迭代器抛出ConcurrentModificationException。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒着任意的,非确定性行为的风险。