Java 8流。除了其他元素外的所有元素

25
我有兴趣找出一种方法,该方法返回一个在另一个列表中不存在的元素列表。
例如:
List<Integer> multiplesOfThree = ... // 3,6,9,12 etc
List<Integer> evens = ... // 2,4,6,8 etc
List<Integer> others = multiplesOfThree.except(evens) // should return a list of elements that are not in the other list

你要如何做到这一点? 我发现了一种方法,但它有些笨拙且难以阅读....

multiplesOfThree.stream()
.filter(intval -> evens.stream().noneMatch(even -> even.intValue() == intval.intValue()))
3个回答

32
你可以使用 Streamfilter 方法,传递一个确保元素不存在于 evens 中的 Predicate
List<Integer> others = multiplesOfThree.stream()
        .filter(i -> !evens.contains(i))
        .collect(Collectors.toList());

但是假设你有一个可变的 List (例如 ArrayList),你甚至不需要使用流,只需要使用 CollectionsremoveAll 方法

multiplesOfThree.removeAll(evens);

removeAll是一个非常优秀的解决方案。 - Alexandre N.
removeAll 从性能角度来看是不错的,特别是在你真正想要改变源集合的情况下。然而,使用不可变方法(返回一个新的集合并删除匹配元素)更加推荐,因为有很多好的理由支持这种方法。 - Per Lundberg

8
您可以使用以下方法:
multipleOfThree.stream()
               .filter(((Predicate<Integer>) evens::contains).negate())

为了更高效地处理大型列表,可以使用更好的算法。
HashSet<Integer> evenSet = new HashSet<>(even);
multipleOfThree.stream()
               .filter(((Predicate<Integer>) evenSet::contains).negate())

5

有几种解决方案。

首先,不使用流,您可以创建一个新列表,并从另一个集合中删除所有元素...

final List<Integer> multiplesOfThree = Arrays.asList(3,6,9,12);
final List<Integer> evens = Arrays.asList(2,4,6,8,10,12);
final List<Integer> others1 = new ArrayList<>(multiplesOfThree);
others1.removeAll(evens);

另一种解决方案是将流通过filter()进行传递:
final List<Integer> others2 = multiplesOfThree
     .stream()
     .filter(x -> !evens.contains(x))
     .collect(Collectors.toList());

在这种情况下,您可以考虑将evens设为Set

最后,您可以修改上面的逻辑,将“偶数”表示为函数而不是所有偶数的集合。这与上面基本相同,但您无需拥有第二个集合。

final List<Integer> others3 = multiplesOfThree
    .stream()
    .filter(x -> x % 2 != 0)
    .collect(Collectors.toList());

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