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

时间:2018-09-12 12:21:53

标签: 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);
    }
});

您知道这样做的更好方法/更有效吗?

4 个答案:

答案 0 :(得分: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);

答案 1 :(得分: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);
    }
}

替代方法是批量方法。

首先,如thisthat答案所示,创建所有要首先传输的元素的副本。

第二,您可以使用

myFirstCollection.removeIf(o -> conditionReturningTrue(o) && mySecondCollection.add(o));

default的{​​{1}}实现在类似于上面的循环中使用removeIf。但是,像Iterator这样的集合提供了自己的ArrayList实现,以克服removeIf循环的二次时间复杂性。

答案 2 :(得分:4)

通过避免使用removeAll(对于某些Collection可能需要二次时间,其中对象查找需要线性搜索,例如List),您可以提高性能。使用Collectors.partitioningBy将原始Collection分为两个List

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

Map<Boolean,List<Object>> partition = 
    myFirstCollection.stream()
                     .collect(Collectors.partitioningBy(o -> conditionReturningTrue(o)));
myFirstCollection.clear();
myFirstCollections.addAll(partition.get(false));
mySecondCollection.addAll(partition.get(true));

另一方面,如果仅将几个元素从myFirstCollection移到mySecondCollection,则此解决方案的效率可能会降低。

答案 3 :(得分:1)

您已经在这里从YCF_L得到了很好的答案:https://stackoverflow.com/a/52295144/9568238

但是我想补充一下,如果您确实按照问题中的描述使用forEach方法,那么stream()是多余的。您可以只做myFirstCollection.forEach(...)

无论哪种方式,我都会回答提到的问题。