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).

Rahul garg
la source
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.

Frontline
la source
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
3
@Xipo a raison. Vérifiez grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…
Lluis Martinez
156

Stream::anyMatch

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

setA.stream().anyMatch(setB::contains)
gpl
la source
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.

CaTalyst.X
la source
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.

Adam111p
la source
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.

Suresh Kumar
la source
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.

Zéychin
la source
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

Plap
la source
2

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

Artem
la source
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