Java 8,Streams查找重复元素

Siv*_*iva 70 java lambda java-8 java-stream

我试图列出整数列表中的重复元素,例如,

List<Integer> numbers = Arrays.asList(new Integer[]{1,2,1,3,4,4});    
Run Code Online (Sandbox Code Playgroud)

使用jdk的Streams 8.有没有人试过.要删除重复项,我们可以使用distinct()api.但是如何找到重复的元素呢?有人可以帮帮我吗?

小智 112

你可以使用Collections.frequency:

numbers.stream().filter(i -> Collections.frequency(numbers, i) >1)
                .collect(Collectors.toSet()).forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

  • 与@OussamaZoghlami相同的O(n ^ 2)性能[回答](http://stackoverflow.com/a/27691091/4856258),虽然可能更简单.然而,这是一个upvote.欢迎来到StackOverflow! (8认同)
  • 如上所述,这是一个^ 2解决方案,其中存在一个简单的线性解决方案.我不会在CR中接受这个. (5认同)
  • 它可能比@Dave选项要慢,但是它更漂亮,因此我将对性能造成影响。 (2认同)
  • @mancocapac是的,它是二次方的,因为频率调用必须访问数字中的每个元素,并且每个元素都被调用。因此,对于每个元素,我们访问每个元素-n ^ 2且不必要地效率低下。 (2认同)

Dav*_*ave 49

你需要一个set(allItems下面)来保存整个数组内容,但这是O(n):

Integer[] numbers = new Integer[] { 1, 2, 1, 3, 4, 4 };
Set<Integer> allItems = new HashSet<>();
Set<Integer> duplicates = Arrays.stream(numbers)
        .filter(n -> !allItems.add(n)) //Set.add() returns false if the item was already in the set.
        .collect(Collectors.toSet());
System.out.println(duplicates); // [1, 4]
Run Code Online (Sandbox Code Playgroud)

  • `filter()`需要一个无状态谓词.你的"解决方案"与javadoc中给出的有状态谓词的例子非常类似:https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#Statelessness (16认同)
  • @IcedD​​ante在一个本地化的案例中你肯定知道流是`sequential()`,它可能是*安全的.在更一般的情况下,流可能是"parallel()",它几乎可以保证以奇怪的方式打破. (6认同)
  • 除了在某些情况下产生意想不到的行为之外,这就像Bloch认为你不应该在Effective Java的第三版中那样混合范式.如果你发现自己写这个,只需使用for循环. (5认同)
  • @MattMcHenry:这是否意味着这个解决方案有可能产生意想不到的行为,或者只是不好的做法? (2认同)
  • 在Hibernate Validator [UniqueElements](https://github.com/hibernate/hibernate-validator/blob/fb6450dfec46d19eb1411362bff9cb2b1ab240b4/engine/src/main/java/org/hibernate/validator/internal/constraintvalidators/ hv / UniqueElementsValidator.java#L64)约束。 (2认同)

Rob*_*bAu 31

基本的例子.上半部分构建频率图,后半部分将其缩小为过滤列表.可能不如Dave的答案那么高效,但更多功能(如果您想要检测到两个等)

     List<Integer> duplicates = IntStream.of( 1, 2, 3, 2, 1, 2, 3, 4, 2, 2, 2 )
       .boxed()
       .collect( Collectors.groupingBy( Function.identity(), Collectors.counting() ) )
       .entrySet()
       .stream()
       .filter( p -> p.getValue() > 1 )
       .map( Map.Entry::getKey )
       .collect( Collectors.toList() );
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是正确的一个imo,因为它是线性的,并且不违反“无状态谓词”规则。 (5认同)

Tag*_*eev 13

我的StreamEx库增强了Java 8流,它提供了一种特殊操作distinct(atLeast),只能保留至少出现指定次数的元素.所以你的问题可以像这样解决:

List<Integer> repeatingNumbers = StreamEx.of(numbers).distinct(2).toList();
Run Code Online (Sandbox Code Playgroud)

在内部它类似于@Dave解决方案,它计算对象,支持其他想要的数量,并且它是并行友好的(它ConcurrentHashMap用于并行化流,但HashMap用于顺序).对于大量数据,您可以使用加速.parallel().distinct(2).

  • 问题是关于Java Streams,而不是第三方库. (16认同)

Tho*_*hew 12

O(n)方式如下:

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 4, 4);
Set<Integer> duplicatedNumbersRemovedSet = new HashSet<>();
Set<Integer> duplicatedNumbersSet = numbers.stream().filter(n -> !duplicatedNumbersRemovedSet.add(n)).collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

在这种方法中,空间复杂性会增加一倍,但这个空间不是浪费; 事实上,我们现在只将一个副本作为一个集合以及另一个集合,同时删除所有重复项.


Ous*_*ami 5

你可以像这样得到重复的:

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 4, 4);
Set<Integer> duplicated = numbers.stream().filter(n -> numbers.stream().filter(x -> x == n).count() > 1).collect(Collectors.toSet());
Run Code Online (Sandbox Code Playgroud)

  • 那不是O(n ^ 2)运算吗? (8认同)
  • 尝试使用`numbers = Arrays.asList(400,400,500,500);` (4认同)