如何根据谓词有效地将对象从一个java集合转移到另一个集合?

Thi*_*sse 3 java collections java-8 java-stream

首先,我希望以前没有问过这个问题.我看了一下,找不到合适的答案:s

当特定条件成立时,我正在寻找一种将一些对象从一个集合移动到另一个集合的有效方法.

目前,我会以非常简单的方式做到这一点,但我担心这可能不是最佳的:

Collection<Object> myFirstCollection;  //let's consider it instanciated and populated
Collection<Object> mySecondCollection; //same for this one

myFirstCollection.stream().forEach(o -> { 
    if ( conditionReturningTrue(o) ) {
        mySecondCollection.add(o);
        myFirstCollection.remove(o);
    }
});
Run Code Online (Sandbox Code Playgroud)

你知道更好的方式/更有效率吗?

YCF*_*F_L 7

为了使其更具可读性,也有Collection::addAllCollection::removeAll在这种情况下使用,你的代码可以是:

// create a new Collection where you use filter to search only the Object you want
Collection<Object> filterdCollection = myFirstCollection.stream()
        .filter(o -> conditionReturningTrue(o))
        .collect(Collectors.toCollection(LinkedHashSet::new));

// use allAll to add all the filtered Object to the second collection
mySecondCollection.addAll(filterdCollection);
// use removeAll to remove all the filtered Object from the first collection
myFirstCollection.removeAll(filterdCollection);
Run Code Online (Sandbox Code Playgroud)

  • @Scorix根据底层列表实现,`addAll`可能比多个`add`操作更快.但这种性能增益(或损失)可以忽略不计.代码可读性不是! (4认同)
  • @ThibaultBezierslafosse如果你想知道哪一个比另一个更好,你必须创建一个基准来检查这个,看看这个[何时在Java中使用LinkedList over ArrayList?](/sf/ask/22590081/)也许它可以回答你的问题 (3认同)
  • 当两个集合都是`ArrayList`s时,`removeAll`可以安静昂贵.但在这里,我们可以控制它.只需将`Collectors.toList()`更改为`Collectors.toCollection(LinkedHashSet :: new)`.然后,`filterdCollection`仍将保持顺序(与`addAll`相关),但支持快速查找(这将使`removeAll`成为线性操作). (3认同)
  • @FedericoPeraltaSchaffner如果处理重复是一个问题,那么你可能最好首先收集到`List`,与`mySecondCollection.addAll(filterdCollection);`一起使用,然后是`myFirstCollection.removeAll(new HashSet <>( filterdCollection));` (2认同)

Hol*_*ger 5

首先,你应该努力争取正确性.对于大多数集合,禁止在迭代时修改源集合.您可能会ConcurrentModificationException尝试一段时间,但即使它碰巧运行没有异常,代码仍然是不正确的.只是这个错误并不总是被检测到(这是一次尽力而为的检查,试图避免浪费太多的性能).这适用于forEach(…),以及stream().forEach(…)for-each循环(for(variable declaration: collection))

迭代时删除元素的唯一支持是通过手动Iterator使用:

for(Iterator<Object> it = myFirstCollection.iterator(); it.hasNext(); ) {
    Object o = it.next();
    if(conditionReturningTrue(o)) {
        it.remove();
        mySecondCollection.add(o);
    }
}
Run Code Online (Sandbox Code Playgroud)

替代方法是批量方法.

首先,像显示在这个那个答案,创建所有元素的副本将被首先转移.

其次,你可以使用

myFirstCollection.removeIf(o -> conditionReturningTrue(o) && mySecondCollection.add(o));
Run Code Online (Sandbox Code Playgroud)

所述default的实施removeIf采用了Iterator以类似于上述的一个循环.但是,集合ArrayList提供了自己的实现removeIf,以克服Iterator循环的二次时间复杂度.