83 votes

Intersection efficace de deux List<String> en Java ?

La question est simple :

J'ai deux listes

List<String> columnsOld = DBUtils.GetColumns(db, TableName);
List<String> columnsNew = DBUtils.GetColumns(db, TableName);

Et j'ai besoin d'obtenir l'intersection de ceux-ci. Existe-t-il un moyen rapide d'y parvenir ?

1 votes

@JohnnyCoder sérieusement ?

0 votes

@Ungeheuer cela ne fonctionne pas si vous voulez que les doublons ne soient inclus que s'ils sont dans les deux listes.

129voto

Roman Points 21807

Vous pouvez utiliser [retainAll](http://docs.oracle.com/javase/7/docs/api/java/util/List.html#retainAll(java.util.Collection) méthode :

columnsOld.retainAll (columnsNew);

14 votes

Note : pour que cela fonctionne avec d'autres objets que les String vous devez bien sûr mettre en œuvre equals y hashCode .

1 votes

Le code est simple mais la complexité algorithmique est faible : O(n×m), contre O(n+m) pour la méthode de la version établie . Avec deux listes de millions d'articles, c'est la différence entre trillions des opérations et millions des opérations.

28voto

shevchik Points 6781

En utilisant l'application Google Goyave bibliothèque :

Sets.intersection(Sets.newHashSet(setA), Sets.newHashSet(setB))

Note : C'est beaucoup plus efficace que de faire naïvement l'intersection avec deux listes : c'est O(n+m), contre O(n×m) pour la méthode de l'intersection. version liste . Avec deux listes de millions d'articles, c'est la différence entre millions des opérations et trillions des opérations.

20voto

bjornhol Points 501

Puisque retainAll ne touche pas à la collection d'arguments, cela serait plus rapide :

List<String> columnsOld = DBUtils.GetColumns(db, TableName); 
List<String> columnsNew = DBUtils.GetColumns(db, TableName); 

for(int i = columnsNew.size() - 1; i > -1; --i){
    String str = columnsNew.get(i);
    if(!columnsOld.remove(str))
        columnsNew.remove(str);
}

L'intersection sera les valeurs laissées dans les colonnesNew. L'élimination des valeurs déjà comparées de columnsOld réduira le nombre de comparaisons nécessaires.

0 votes

Mais votre code devrait définitivement être extrait dans une nouvelle méthode séparée parce qu'il n'est absolument pas clair de ce code ce qu'il fait. Et je n'aurais pas non plus refusé un test unitaire supplémentaire pour ce code.

0 votes

Je suis d'accord, une bonne séparation des méthodes, le nommage et les tests unitaires sont toujours la règle numéro un.

0 votes

Cette méthode ne devrait-elle pas ajouter les éléments qui ne peuvent pas être trouvés dans les columnsOld aux columnsNew ? Il semble que ces éléments seront manquants dans le résultat.

8voto

Gigas Points 39

Et si

private List<String> intersect(List<String> A, List<String> B) {
    List<String> rtnList = new LinkedList<>();
    for(String dto : A) {
        if(B.contains(dto)) {
            rtnList.add(dto);
        }
    }
    return rtnList;
}

7 votes

Si B contient des éléments qui ne sont pas contenus dans A, il n'est pas nécessaire d'itérer sur ces éléments car nous essayons de trouver tous les éléments à la fois dans A et dans B.

3voto

Deutro Points 316

Il existe une méthode intéressante avec les flux qui permet de faire cela en une ligne de code et vous pouvez avoir deux listes qui ne sont pas du même type, ce qui n'est pas possible avec la méthode containsAll :

columnsOld.stream().filter(c -> columnsNew.contains(c)).collect(Collectors.toList());

Un exemple pour les listes de différents types. Si vous avez une relation entre foo et bar et que vous pouvez obtenir un objet bar à partir de foo, vous pouvez modifier votre flux :

List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));

fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());

2 votes

c -> columnsNew.contains(c) lambda peut être réécrit de manière plus concise comme une référence de méthode : columnsNew::contains .

1 votes

Mais cela ne fonctionnera-t-il pas en temps O(n^2) ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X