web-dev-qa-db-fra.com

Java 8, Streams pour trouver les éléments en double

J'essaie d'énumérer les éléments en double dans la liste entière, par exemple,

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

using Streams of jdk 8. Est-ce que quelqu'un a essayé? Pour supprimer les doublons, nous pouvons utiliser l’API distincte (). Mais qu'en est-il de trouver les éléments dupliqués? Quelqu'un peut m'aider?

47
Siva

Vous pouvez utiliser Collections.frequency :

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

Vous avez besoin d'un ensemble (allItems ci-dessous) pour contenir tout le contenu du tableau, mais c'est 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

Exemple de base. La première moitié construit la carte de fréquence, la seconde moitié la réduit à une liste filtrée. Probablement pas aussi efficace que la réponse de Dave, mais plus polyvalent (comme si vous voulez détecter exactement deux, etc.)

    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

Mon StreamEx bibliothèque qui améliore les flux Java 8 fournit une opération spéciale distinct(atLeast) qui ne peut conserver que les éléments apparaissant au moins le nombre de fois spécifié. Donc, votre problème peut être résolu comme ceci:

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

En interne, cette solution est similaire à la solution @Dave: elle compte les objets, prend en charge les autres quantités souhaitées et est compatible avec le parallèle (elle utilise ConcurrentHashMap pour le flux parallélisé, mais HashMap pour la méthode séquentielle). Pour les grandes quantités de données, vous pouvez accélérer le processus en utilisant .parallel().distinct(2).

11
Tagir Valeev

Une manière O(n) serait comme ci-dessous:

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

La complexité de l’espace irait le double dans cette approche, mais cet espace n’est pas un gaspillage; En fait, nous avons maintenant la copie uniquement en tant que jeu, ainsi qu’un autre avec tous les doublons supprimés.

8
Thomas Mathew

Vous pouvez obtenir le dupliqué comme ceci:

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
Oussama Zoghlami

Je pense que les solutions de base à la question devraient être les suivantes:

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

eh bien, il n'est pas recommandé d'effectuer une opération de filtrage, mais pour une meilleure compréhension, je l'ai utilisé. En outre, il devrait y avoir une filtration personnalisée dans les versions futures.

3
Prashant

la création d'une carte ou d'un flux supplémentaire prend du temps et de l'espace…

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]


… et pour la question dont on prétend être un [duplicata]

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

Un multiset est une structure qui conserve le nombre d'occurrences pour chaque élément. Utilisation de la mise en œuvre de goyave:

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

Je pense avoir une bonne solution pour résoudre ce problème - Liste => Liste avec regroupement par Something.a & Something.b . Il existe une définition étendue:

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

}

classe A, liste1 ce ne sont que des données entrantes - la magie est dans les Objects.hash (...) :)

Qu'en est-il de la vérification des index?

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

Essayez cette solution:

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

Si vous avez seulement besoin de détecter la présence de doublons (au lieu de les lister, comme le voulait le PO), convertissez-les simplement en liste et en ensemble, puis comparez les tailles:

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

J'aime cette approche car il y a moins de place pour les erreurs.

0
Patrick

Devez-vous utiliser les idiomes Java 8 (steams)? Une solution simple consisterait peut-être à déplacer la complexité vers une structure de données semblable à une carte qui contienne des nombres comme clé (sans se répéter) et les moments où elle se présente comme une valeur. Vous pouvez les utiliser pour modifier cette carte et ne faire que quelque chose avec ces nombres qui sont des occurrences> 1.

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