Quelque chose comme «contient tout» pour l'ensemble Java?


307

J'ai deux ensembles, A et B, du même type.

Je dois trouver si A contient un élément de l'ensemble B.

Quelle serait la meilleure façon de le faire sans itérer sur les décors? La bibliothèque Set a contains(object)et containsAll(collection), mais pas containsAny(collection).


4
Essayez-vous d'éviter d'itérer pour des raisons d'efficacité ou pour la propreté du code?
yshavit

Réponses:


527

Ça ne Collections.disjoint(A, B)marcherait pas ? De la documentation:

Renvoie truesi les deux collections spécifiées n'ont aucun élément en commun.

Ainsi, la méthode retourne falsesi les collections contiennent des éléments communs.


17
Préférez ceci aux autres solutions car il ne modifie aucun des ensembles ou n'en crée un nouveau.
devconsole

7
Et est JRE standard, et fonctionne avec toutes les collections, pas seulement ensemble.
Pierre Henry

4
Je ne pense pas que ce soit le plus rapide, il ne court-circuitera pas lorsque le premier élément de l'intersection sera trouvé.
Ben Horner

7
En fait, il court-circuitera dès qu'il trouvera le premier élément commun
Xipo


156

Stream::anyMatch

Depuis Java 8, vous pouvez utiliser Stream::anyMatch.

setA.stream().anyMatch(setB::contains)

1
Ceci est exactement ce que je cherchais! Merci :-) Je ne savais pas non plus que vous pouviez utiliser des variables avec la syntaxe ::!
dantiston

1
@blevert, pourriez-vous expliquer ce qui se passe dans anyMatch?
Cristiano

8
@Cristiano ici, anyMatchdiffusera tous les éléments setAet les appellera setB.contains()tous. Si "true" est renvoyé pour l'un des éléments, l'expression dans son ensemble sera évaluée à true. J'espère que cela vous a aidé.
Alex Vulaj


31

Une bonne façon d'implémenter containsAny pour les ensembles consiste à utiliser Guava Sets.intersection () .

containsAnyretournerait un boolean, donc l'appel ressemble à:

Sets.intersection(set1, set2).isEmpty()

Cela renvoie vrai si les ensembles sont disjoints, sinon faux. La complexité temporelle de ceci est probablement légèrement meilleure que retenue Tout parce que vous n'avez pas à faire de clonage pour éviter de modifier votre ensemble d'origine.


3
Le seul inconvénient de cette approche est que vous devez inclure des bibliothèques de goyaves. Ce qui, je pense, n'est pas un inconvénient car les API de collecte Google sont très solides.
Mohammad Adnan

@DidierL la plupart des fonctions de l'utilitaire Guava Collections, y compris celle-ci, renvoient des vues des structures de données. Il n'y a donc pas de "construction de l'ensemble" à s'inquiéter dans ce cas. L'implémentation est intéressante à lire ici, et / ou voir le javadoc: google.github.io/guava/releases/21.0/api/docs/com/google/common/…
chut

@MohammadAdnan Un autre inconvénient est qu'il calcule l'intersection complète - si set1 et set2 sont très grands, cela nécessiterait beaucoup plus de ressources (à la fois CPU et mémoire) que de simplement vérifier s'ils ont un élément en commun.
Marxama


16

J'utilise org.apache.commons.collections.CollectionUtils

CollectionUtils.containsAny(someCollection1, someCollection2)

C'est tout! Renvoie true si au moins un élément se trouve dans les deux collections.

Simple à utiliser et le nom de la fonction est plus suggestif.


5

Utiliser retainAll()dans l'interface Set. Cette méthode fournit une intersection d'éléments communs aux deux ensembles. Consultez la documentation de l'API pour plus d'informations.


Si le but d'éviter l'itération est pour l'efficacité, retainAllcela n'aidera probablement pas. Sa mise en œuvre en AbstractCollectionitérations.
yshavit

1
yshavit est correct. Étant donné que l'OP cherche à voir si un élément existe dans les deux ensembles, un algorithme approprié aurait un O(1)temps d'exécution dans le meilleur des cas, alors retainAllqu'il aurait quelque chose dans le sens d'un O(N)(cela dépendrait de la taille d'un seul ensemble) meilleur temps de fonctionnement.
Zéychin

3

Je recommanderais de créer un à HashMappartir de l'ensemble A, puis d'itérer à travers l'ensemble B et de vérifier si un élément de B est dans A. Cela fonctionnerait dans le O(|A|+|B|)temps (car il n'y aurait pas de collisions), alors qu'il retainAll(Collection<?> c)doit fonctionner dans le O(|A|*|B|)temps.


3

Il y a une méthode un peu approximative pour le faire. Si et seulement si l'ensemble A contient un élément B que l'appel

A.removeAll(B)

modifiera l'ensemble A. Dans cette situation, removeAll renverra true (comme indiqué dans removeAll docs ). Mais vous ne voulez probablement pas modifier l'ensemble A, vous pouvez donc penser à agir sur une copie, comme ceci:

new HashSet(A).removeAll(B)

et la valeur de retour sera vraie si les ensembles ne sont pas distincts, c'est-à-dire qu'ils ont une intersection non vide.

Voir aussi les collections Apache Commons


2

Vous pouvez utiliser la méthode retenueAll et obtenir l'intersection de vos deux ensembles.


Dans la plupart des cas, il faut conserver l'ensemble d'origine, donc pour l'utiliser, retainAllil est nécessaire de faire une copie de l'ensemble d'origine. Ensuite, il est plus efficace à utiliser HashSetcomme suggéré par Zéychin .
Petr Pudlák

C'est un changement d'état, pas un contrôle d'état
Ben
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.