使用Java 8安全地从集合中移除项目

3

我曾尝试使用 forEach 迭代一个集合,但意识到在该集合上调用 remove(Object o) 是不安全的,可能会导致 ConcurrentModificationException 异常。这是我的尝试:

public void removeMatchup(Set<Player> players) {
        predefinedMatchups.stream().filter(m -> m.getPlayers().equals(players)).forEach(m -> predefinedMatchups.remove(m));
}

所以我将它改为了这样:

public void removeMatchup(Set<Player> players) {
    Iterator<Matchup> iterator = predefinedMatchups.iterator();
    while (iterator.hasNext())
        if (iterator.next().getPlayers().equals(players))
            iterator.remove();
}

我非常喜欢Streams的简洁性,这就是为什么我正在重新设计我的整个项目以包含Java 8的新功能。

是否有一种解决方法可以在执行安全删除时使用Streams?


你在“安全”一词下的意思是什么?你是不是不想在实际使用只有一个线程进行删除时遇到ConcurrentModificationException,还是你指的是线程安全? - Les
2个回答

10

由于SetCollection,因此我们可以使用removeIf(Predicate<? super E> filter)方法(例如默认的方法内部使用Iterator和它的remove方法,就像您第二个例子中的一样)。

409默认布尔值 removeIf(Predicate<? super E> filter) {
410 Objects.requireNonNull(filter);
411 布尔值 removed = false;
412 final 迭代器<E> each = iterator();
413 while (each.hasNext()) {
414 if (filter.test(each.next())) {
415 each.remove();
416 removed = true;
417 }
418 }
419 return removed;
420 }

所以你的代码可以像这样:
public void removeMatchup(Set<Player> players) {
    predefinedMatchups.removeIf(m -> m.getPlayers().equals(players));
}

如果我想要连接一个触发器,以便在确实删除一个或多个项目时触发它,我该怎么做?在我的情况下,如果从集合中删除一个项目,我需要更新另一个不同的集合作为结果。这是否仍然可以使用Java 8完成,还是应该回到我的原始解决方案? - dabadaba
1
@dabadaba Lambda 可以包含许多表达式,其中一个可能是添加您要删除的项目。因此,您可以尝试类似于 removeIf(m->{boolean result = m.getPlayers().equals(players); if (result) otherCollection.add(m); return result;}) 的代码。 - Pshemo
我猜测每次返回的result值才是决定removeIf方法是否触发删除项目的关键,我错了吗? - dabadaba
1
是的。正如您在更新的答案中所看到的,删除是基于Predicate#test的结果,并且lambda为该test方法提供了主体。因此,我的评论中的解决方案将首先向otherCollection添加元素,然后再将其删除。 - Pshemo
2
@Pshemo:在removeIf中添加到另一个集合会导致反向操作。因此,更简单的方法是使用Set<...> toRemove = orig.stream().filter(...).collect(toSet()); orig.removeAll(toRemove);。除此之外,无论默认实现是否使用Iterator,每个集合都可以覆盖它。重要的是该方法的契约 - Holger

1
尝试一下:

removeIf

public void removeMatchup(Set<Player> players) {
        predefinedMatchups.removeIf(
            value-> players.conainsAll(value.getPlayers())
        );
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接