So etwas wie "enthält welche" für Java-Set?

307

Ich habe zwei Sätze, A und B, vom gleichen Typ.

Ich muss herausfinden, ob A ein Element aus der Menge B enthält.

Was wäre der beste Weg, dies zu tun, ohne die Sets zu durchlaufen? Die Set-Bibliothek hat contains(object)und containsAll(collection), aber nicht containsAny(collection).

Rahul garg
quelle
4
Versuchen Sie, Iterationen aus Effizienzgründen oder aus Gründen der Code-Sauberkeit zu vermeiden?
Yshavit

Antworten:

527

Würde nicht Collections.disjoint(A, B)funktionieren? Aus der Dokumentation:

Gibt zurück, truewenn die beiden angegebenen Sammlungen keine gemeinsamen Elemente haben.

Daher gibt die Methode zurück, falsewenn die Sammlungen gemeinsame Elemente enthalten.

Front
quelle
17
Ziehen Sie dies den anderen Lösungen vor, da keine der Gruppen geändert oder eine neue erstellt wird.
Devconsole
7
Und ist Standard-JRE und funktioniert mit allen Sammlungen, nicht nur festgelegt.
Pierre Henry
4
Ich denke nicht, dass dies am schnellsten ist, es wird nicht kurzschließen, wenn das erste Element der Kreuzung gefunden wird.
Ben Horner
7
Tatsächlich wird es kurzschließen, sobald es das erste gemeinsame Element findet
Xipo
3
@ XPo ist richtig. Überprüfen Sie grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/…
Lluis Martinez
156

Stream::anyMatch

Seit Java 8 können Sie verwenden Stream::anyMatch.

setA.stream().anyMatch(setB::contains)
gpl
quelle
1
Genau das habe ich gesucht! Danke :-) Ich wusste auch nicht, dass Sie Variablen mit der :: -Syntax verwenden können!
Dantiston
1
@blevert, kannst du erklären, was in anyMatch passiert?
Cristiano
7
@Cristiano hier anyMatchwird alle Elemente streamen und alle setAaufrufen setB.contains(). Wenn für eines der Elemente "true" zurückgegeben wird, wird der Ausdruck als Ganzes als true ausgewertet. Hoffe das hat geholfen.
Alex Vulaj
31

Eine gute Möglichkeit, includesAny für Sets zu implementieren, ist die Verwendung von Guava Sets.intersection () .

containsAnywürde a zurückgeben boolean, so sieht der Anruf aus wie:

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

Dies gibt true zurück, wenn die Mengen disjunkt sind, andernfalls false. Die zeitliche Komplexität ist wahrscheinlich etwas besser als bei keepAll, da Sie kein Klonen durchführen müssen, um Änderungen an Ihrem ursprünglichen Satz zu vermeiden.

CaTalyst.X
quelle
3
Der einzige Nachteil dieses Ansatzes besteht darin, dass Sie Guavenbibliotheken einbeziehen müssen. Was meiner Meinung nach kein Nachteil ist, da die Google Collection-APIs sehr stark sind.
Mohammad Adnan
@DidierL Die meisten Funktionen des Guava Collections-Dienstprogramms, einschließlich dieser, geben Ansichten der Datenstrukturen zurück. In diesem Fall gibt es also kein "Bauen des Sets", um das man sich Sorgen machen muss. Die Implementierung ist interessant, hier zu lesen und / oder das Javadoc zu
chut
@MohammadAdnan Ein weiterer Nachteil ist, dass die vollständige Schnittmenge berechnet wird. Wenn set1 und set2 sehr groß sind, ist dies viel ressourcenintensiver (sowohl in Bezug auf die CPU als auch in Bezug auf den Speicher), als nur zu prüfen, ob sie ein gemeinsames Element haben.
Marxama
16

Ich benutze org.apache.commons.collections.CollectionUtils

CollectionUtils.containsAny(someCollection1, someCollection2)

Das ist alles! Gibt true zurück, wenn sich mindestens ein Element in beiden Sammlungen befindet.

Einfach zu bedienen, und der Name der Funktion ist aussagekräftiger.

Adam111p
quelle
5

Verwendung retainAll()in der Set-Oberfläche. Diese Methode bietet einen Schnittpunkt von Elementen, die in beiden Mengen gemeinsam sind. Weitere Informationen finden Sie in den API-Dokumenten.

Suresh Kumar
quelle
Wenn der Punkt, an dem die Iteration vermieden wird, der Effizienz dient, wird dies retainAllwahrscheinlich nicht helfen. Seine Implementierung in AbstractCollectionIterationen.
Yshavit
1
yshavit ist richtig. Da der OP schaut , um zu sehen , ob irgendein Element in beiden Sätzen vorhanden ist , wäre ein geeigneter Algorithmus einen hat O(1)im besten Fall Laufzeit, während retainAlletwas entlang der Linien eines hätte O(N)(es wäre nur ein Satz von der Größe abhängig) Best-Case-Laufzeit.
Zéychin
3

Ich würde empfehlen, ein HashMapaus Satz A zu erstellen und dann Satz B zu durchlaufen und zu überprüfen, ob sich ein Element von B in A befindet. Dies würde O(|A|+|B|)zeitlich ausgeführt (da es keine Kollisionen geben würde), während retainAll(Collection<?> c)es O(|A|*|B|)rechtzeitig ausgeführt werden muss.

Zéychin
quelle
3

Dafür gibt es eine etwas grobe Methode. Genau dann, wenn die A-Menge ein B-Element als den Aufruf enthält

A.removeAll(B)

ändert das A-Set. In dieser Situation gibt removeAll true zurück (wie in den Dokumenten zum Entfernen von all angegeben ). Aber wahrscheinlich möchten Sie das A-Set nicht ändern, damit Sie auf eine Kopie wie folgt reagieren können:

new HashSet(A).removeAll(B)

und der Rückgabewert ist wahr, wenn die Mengen nicht verschieden sind, das heißt, sie haben einen nicht leeren Schnittpunkt.

Siehe auch Apache Commons-Sammlungen

Plap
quelle
2

Sie können die RetainAll- Methode verwenden und den Schnittpunkt Ihrer beiden Sätze ermitteln.

Artem
quelle
In den meisten Fällen muss das Originalset aufbewahrt werden. Um es verwenden zu retainAllkönnen, muss eine Kopie des Originalsets erstellt werden. Dann ist es effizienter zu verwenden, HashSetwie von Zéychin vorgeschlagen .
Petr Pudlák
Das ist eine Zustandsänderung, keine Zustandsprüfung
Ben