的問題是,所述x.removeAll(y)
操作O(N×M),其中Ñ是集合x
的大小,和米是集合的大小y
(即,O( | x |×| y |))。
removeAll
方法基本上是遍歷整個列表中的每個元素y
,檢查x
中的每個元素是否恰好相等,如果是,則刪除它。如果你一次就能做到這一點,效率會更高。
假設你正在使用Java 8,有一個更有效的方式來做到這一點:
List<Integer> xs = new ArrayList<>();
// TODO: initialize xs with a bunch of values
List<Integer> ys = new ArrayList<>();
// TODO: initialize ys with a bunch of values
Set<Integer> ysSet = new HashSet<>(ys);
List<Integer> xsPrime = xs.stream()
.filter(x -> !ysSet.contains(x))
.collect(Collectors.toList());
爲大小100K的xs
和大小66k
的ys
,使用removeAll
花了大約5500ms,而使用以上方法只需要約8ms。由於removeAll
的二次複雜性,我預計當您擴展到200k時,差異會更加明顯。
與此相反,以上所使用的過濾器版本複雜性將是O(N + M),因爲它O(米)建立的所有值的HashSet
在ys
,然後O(n)遍歷所有xs
的值以確保新的ysSet
中沒有包含任何值。 (當然,這是假設一個HashSet
查找是O(1)的。)
再回首你的問題,我知道你已經在使用filter
......在這種情況下,我建議只是反轉的過濾邏輯,然後重新傳入的列表的值過濾的值:
public List<> filterList(List<> listToBeFiltered){
List<> filteredList = listToBeFiltered.parallelStream()
.filter(/* some inverted filtering logic */)
.collect(Collectors.toList());
listToBeFiltered.clear();
listToBeFiltered.addAll(filteredList);
return listToBeFiltered;
}
如果你不需要變異原始列表,那麼你可以直接返回filteredList
。 (這將是我的首選解決方案呢。)
我只是又跑我的測試,這一次我補充說,使用一個循環,而不是流的另一個版本:
Set<Integer> ysSet = new HashSet<>(ys);
List<Integer> xsPrime = new ArrayList<>();
for (Integer x : xs) {
if (!ysSet.contains(x)) {
xsPrime.add(x);
}
}
return xsPrime;
這個版本中完成大約7ms而不是8ms。由於這隻比流版本稍快(特別是考慮到使用removeAll
的原始版本慢了3個數量級),我會堅持使用流版本 - 尤其是因爲您可以利用並行性(因爲您已經在做與parallelStream
)。