wake-up-neo.net

Java 8, Streams, um die doppelten Elemente zu finden

Ich versuche, doppelte Elemente in der Integer-Liste aufzulisten, zum Beispiel:

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

streams of jdk 8 verwenden. Hat jemand ausprobiert. Um die Duplikate zu entfernen, können wir die distinct () - API verwenden. Aber was ist mit dem Auffinden der duplizierten Elemente? Kann mir jemand helfen?

47
Siva

Sie können Collections.frequency verwenden:

numbers.stream().filter(i -> Collections.frequency(numbers, i) >1)
                .collect(Collectors.toSet()).forEach(System.out::println);
90
Bao Dinh

Sie benötigen einen Satz (allItems unten), um den gesamten Inhalt des Arrays aufzunehmen, dies ist jedoch 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]
34
Dave

Grundlegendes Beispiel. Die erste Hälfte erstellt eine Frequenzkarte, die zweite Hälfte reduziert sie auf eine gefilterte Liste. Wahrscheinlich nicht so effizient wie Daves Antwort, aber vielseitiger (wenn Sie genau zwei erkennen möchten usw.)

    List<Integer> duplicates = IntStream.of( 1, 2, 3, 2, 1, 2, 3, 4, 2, 2, 2 )
                                            .boxed()
                                            .collect( Collectors.groupingBy( c -> c, Collectors.counting() ) )
                                            .entrySet()
                                            .stream()
                                            .filter( p -> p.getValue() > 1 )
                                            .map( e -> e.getKey() )
                                            .collect( Collectors.toList() );
18
RobAu

Meine StreamEx - Bibliothek, die die Java 8-Streams erweitert, bietet eine spezielle Operation distinct(atLeast) , die nur Elemente enthalten kann, die mindestens die angegebene Anzahl von Malen enthalten. So kann Ihr Problem so gelöst werden:

List<Integer> repeatingNumbers = StreamEx.of(numbers).distinct(2).toList();

Intern ist sie der @Dave-Lösung ähnlich. Sie zählt Objekte, um andere gewünschte Mengen zu unterstützen, und ist parallel-freundlich (sie verwendet ConcurrentHashMap für parallelisierten Stream, aber HashMap für sequentiell). Bei großen Datenmengen können Sie mit .parallel().distinct(2) eine Beschleunigung erhalten.

11
Tagir Valeev

Ein O(n) Weg wäre wie folgt:

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());

Die Raumkomplexität würde sich bei diesem Ansatz verdoppeln, aber dieser Raum ist keine Verschwendung; Tatsächlich haben wir das Duplikat jetzt nur noch als Set sowie ein weiteres Set, bei dem alle Duplikate entfernt wurden.

8
Thomas Mathew

Sie können das Duplikat wie folgt erhalten:

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());
4

Ich denke, grundlegende Lösungen für die Frage sollten wie folgt aussehen:

Supplier supplier=HashSet::new; 
HashSet has=ls.stream().collect(Collectors.toCollection(supplier));

List lst = (List) ls.stream().filter(e->Collections.frequency(ls,e)>1).distinct().collect(Collectors.toList());

nun, es ist nicht empfehlenswert, eine Filteroperation durchzuführen, aber zum besseren Verständnis habe ich es verwendet, außerdem sollte es in zukünftigen Versionen eine benutzerdefinierte Filterung geben.

3
Prashant

Das Erstellen einer zusätzlichen Karte oder eines Streams ist zeit- und platzaufwändig…

Set<Integer> duplicates = numbers.stream().collect( Collectors.collectingAndThen(
  Collectors.groupingBy( Function.identity(), Collectors.counting() ),
  map -> {
    map.values().removeIf( cnt -> cnt < 2 );
    return( map.keySet() );
  } ) );  // [1, 4]


… und für dessen Frage wird behauptet, ein [Duplikat] zu sein

public static int[] getDuplicatesStreamsToArray( int[] input ) {
  return( IntStream.of( input ).boxed().collect( Collectors.collectingAndThen(
      Collectors.groupingBy( Function.identity(), Collectors.counting() ),
      map -> {
        map.values().removeIf( cnt -> cnt < 2 );
        return( map.keySet() );
      } ) ).stream().mapToInt( i -> i ).toArray() );
}
2
Kaplan

Ein Multiset ist eine Struktur, die die Anzahl der Vorkommen für jedes Element aufrechterhält. Verwendung der Guava-Implementierung:

Set<Integer> duplicated =
        ImmutableMultiset.copyOf(numbers).entrySet().stream()
                .filter(entry -> entry.getCount() > 1)
                .map(Multiset.Entry::getElement)
                .collect(Collectors.toSet());
1
numéro6

Ich denke, ich habe eine gute Lösung, um ein Problem wie dieses zu beheben - Liste => Liste mit Gruppierung nach Something.a & Something.b .

public class Test {

    public static void test() {

        class A {
            private int a;
            private int b;
            private float c;
            private float d;

            public A(int a, int b, float c, float d) {
                this.a = a;
                this.b = b;
                this.c = c;
                this.d = d;
            }
        }


        List<A> list1 = new ArrayList<A>();

        list1.addAll(Arrays.asList(new A(1, 2, 3, 4),
                new A(2, 3, 4, 5),
                new A(1, 2, 3, 4),
                new A(2, 3, 4, 5),
                new A(1, 2, 3, 4)));

        Map<Integer, A> map = list1.stream()
                .collect(HashMap::new, (m, v) -> m.put(
                        Objects.hash(v.a, v.b, v.c, v.d), v),
                        HashMap::putAll);

        list1.clear();
        list1.addAll(map.values());

        System.out.println(list1);
    }

}

klasse A, Liste1 Es sind nur eingehende Daten - Magie ist in Objects.hash (...) :)

Wie sieht es mit der Überprüfung von Indizes aus?

        numbers.stream()
            .filter(integer -> numbers.indexOf(integer) != numbers.lastIndexOf(integer))
            .collect(Collectors.toSet())
            .forEach(System.out::println);
0
bagom

Versuchen Sie diese Lösung:

public class Anagramm {

public static boolean isAnagramLetters(String Word, String anagramm) {
    if (anagramm.isEmpty()) {
        return false;
    }

    Map<Character, Integer> mapExistString = CharCountMap(Word);
    Map<Character, Integer> mapCheckString = CharCountMap(anagramm);
    return enoughLetters(mapExistString, mapCheckString);
}

private static Map<Character, Integer> CharCountMap(String chars) {
    HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>();
    for (char c : chars.toCharArray()) {
        if (charCountMap.containsKey(c)) {
            charCountMap.put(c, charCountMap.get(c) + 1);
        } else {
            charCountMap.put(c, 1);
        }
    }
    return charCountMap;
}

static boolean enoughLetters(Map<Character, Integer> mapExistString, Map<Character,Integer> mapCheckString) {
    for( Entry<Character, Integer> e : mapCheckString.entrySet() ) {
        Character letter = e.getKey();
        Integer available = mapExistString.get(letter);
        if (available == null || e.getValue() > available) return false;
    }
    return true;
}

}
0
Ilia Galperin

Wenn Sie nur das Vorhandensein von Duplikaten erkennen müssen (anstatt sie aufzulisten, was das OP wollte), konvertieren Sie sie einfach in eine Liste und ein Set und vergleichen Sie dann die Größen:

    List<Integer> list = ...;
    Set<Integer> set = new HashSet<>(list);
    if (list.size() != set.size()) {
      // duplicates detected
    }

Ich mag diesen Ansatz, weil es weniger Orte für Fehler gibt.

0
Patrick

Müssen Sie die Java 8-Idiome (Dämpfe) verwenden? Eine einfache Lösung wäre, die Komplexität auf eine mapähnliche Datenstruktur zu verschieben, die Zahlen als Schlüssel enthält (ohne sich zu wiederholen) und die Zeiten, zu denen sie als Wert auftritt. Sie könnten diese Karte iterieren und nur etwas mit den Zahlen tun, die Vorkommen> 1 sind.

import Java.lang.Math;
import Java.util.Arrays;
import Java.util.List;
import Java.util.Map;
import Java.util.HashMap;
import Java.util.Iterator;

public class RemoveDuplicates
{
  public static void main(String[] args)
  {
   List<Integer> numbers = Arrays.asList(new Integer[]{1,2,1,3,4,4});
   Map<Integer,Integer> countByNumber = new HashMap<Integer,Integer>();
   for(Integer n:numbers)
   {
     Integer count = countByNumber.get(n);
     if (count != null) {
       countByNumber.put(n,count + 1);
     } else {
       countByNumber.put(n,1);
     }
   }
   System.out.println(countByNumber);
   Iterator it = countByNumber.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry pair = (Map.Entry)it.next();
        System.out.println(pair.getKey() + " = " + pair.getValue());
    }
  }
}
0
Victor