我在测试使用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));
}
答案 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。因此,面对并发修改,迭代器会快速而干净地失败,而不是在未来不确定的时间冒着任意的,非确定性行为的风险。