Qualcosa come "contiene qualsiasi" per il set Java?


307

Ho due set, A e B, dello stesso tipo.

Devo scoprire se A contiene qualche elemento dell'insieme B.

Quale sarebbe il modo migliore per farlo senza iterare sui set? La libreria Set ha contains(object)e containsAll(collection), ma non containsAny(collection).


4
Stai cercando di evitare l'iterazione per motivi di efficienza o per la pulizia del codice?
yshavit,

Risposte:


527

Non Collections.disjoint(A, B)funzionerebbe? Dalla documentazione:

Restituisce truese le due raccolte specificate non hanno elementi in comune.

Pertanto, il metodo restituisce falsese le raccolte contengono elementi comuni.


17
Preferisci questo alle altre soluzioni perché non modifica nessuno dei set né ne crea uno nuovo.
devconsole,

7
Ed è JRE standard, e funziona con qualsiasi collezione, non solo impostata.
Pierre Henry,

4
Non penso che questo sia il più veloce, non cortocircuiterà quando viene trovato il primo elemento dell'intersezione.
Ben Horner,

7
In realtà si cortocircuita non appena trova il primo elemento comune
Xipo


156

Stream::anyMatch

Da Java 8 potresti usare Stream::anyMatch.

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

1
Questo e 'esattamente quello che stavo cercando! Grazie :-) Inoltre non sapevo che avresti potuto usare le variabili con la sintassi ::!
Dantiston,

1
@blevert, potresti spiegare cosa succede all'interno di anyMatch?
Cristiano,

8
@Cristiano qui, anyMatchtrasmetterà in streaming tutti gli elementi setAe li chiamerà setB.contains()tutti. Se "true" viene restituito per uno qualsiasi degli elementi, l'espressione nel suo insieme verrà valutata come vera. Spero che questo abbia aiutato.
Alex Vulaj,


31

Un buon modo per implementare contengaAny per i set è usare Guava Sets.intersection () .

containsAnyrestituire a boolean, quindi la chiamata è simile a:

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

Questo restituisce vero se gli insiemi sono disgiunti, altrimenti falso. La complessità temporale di questo è probabilmente leggermente migliore rispetto a keepAll perché non è necessario eseguire alcuna clonazione per evitare di modificare il set originale.


3
L'unico svantaggio dell'uso di questo approccio è che devi includere librerie guava. Il che penso non sia uno svantaggio perché le API di raccolta di Google sono molto potenti.
Mohammad Adnan,

@DidierL la maggior parte delle funzioni di utilità di Guava Collections, inclusa questa, restituisce viste delle strutture di dati. Quindi non c'è "costruire il set" di cui preoccuparsi in questo caso. L'implementazione è interessante da leggere qui e / o vedere il javadoc: google.github.io/guava/releases/21.0/api/docs/com/google/common/…
chut

@MohammadAdnan Un altro svantaggio è che calcola l'intersezione completa - se set1 e set2 sono molto grandi, ciò richiederebbe molte più risorse (sia CPU che memoria) rispetto al semplice controllo della presenza di elementi in comune.
Marxama,


16

Uso org.apache.commons.collections.CollectionUtils

CollectionUtils.containsAny(someCollection1, someCollection2)

Questo è tutto! Restituisce vero se almeno un elemento si trova in entrambe le raccolte.

Semplice da usare e il nome della funzione è più suggestivo.


5

Utilizzare retainAll()nell'interfaccia Set. Questo metodo fornisce un'intersezione di elementi comuni in entrambi gli insiemi. Consulta i documenti API per ulteriori informazioni.


Se il punto di evitare l'iterazione è per l'efficienza, retainAllprobabilmente non sarà di aiuto. La sua implementazione in AbstractCollectioniterate.
yshavit,

1
yshavit è corretto. Dato che l'OP sta cercando di vedere se esiste un elemento in entrambi gli insiemi, un algoritmo adeguato avrebbe un O(1)tempo di esecuzione nel migliore dei casi, mentre retainAllavrebbe qualcosa lungo le linee di un O(N)(dipenderebbe dalla dimensione di solo 1 insieme) tempo di esecuzione nel migliore dei casi.
Zéychin,

3

Vorrei raccomandare di creare un HashMapset di set A, quindi di scorrere tra set di set B e di verificare se qualche elemento di B è in A. Questo verrebbe eseguito in O(|A|+|B|)tempo (poiché non ci sarebbero collisioni), mentre retainAll(Collection<?> c)dovrebbe essere eseguito in O(|A|*|B|)tempo.


3

C'è un metodo un po 'approssimativo per farlo. Se e solo se l'insieme A contiene un elemento di B rispetto alla chiamata

A.removeAll(B)

modificherà il set A. In questa situazione, removeAll restituirà true (come indicato nella documentazione di removeAll ). Ma probabilmente non vuoi modificare il set A, quindi potresti pensare di agire su una copia, in questo modo:

new HashSet(A).removeAll(B)

e il valore di ritorno sarà vero se gli insiemi non sono distinti, ovvero hanno un'intersezione non vuota.

Vedi anche le raccolte Apache Commons


2

È possibile utilizzare il metodo retainAll e ottenere l'intersezione dei due set.


Nella maggior parte dei casi è necessario conservare il set originale, quindi per retainAllpoterlo utilizzare è necessario effettuare una copia del set originale. Quindi è più efficiente da usare HashSetcome suggerito da Zéychin .
Petr Pudlák,

Questo è un cambio di stato, non un controllo delle condizioni
Ben
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.