Controlla se un elenco contiene elementi dell'altro


105

Ho due elenchi con diversi oggetti al loro interno.

List<Object1> list1;
List<Object2> list2;

Voglio verificare se l'elemento da list1 esiste in list2, in base a un attributo specifico (Object1 e Object2 hanno (tra gli altri), un attributo reciproco (con tipo Long), denominato attributeSame).

adesso, lo faccio in questo modo:

boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

Ma penso che ci sia un modo migliore e più veloce per farlo :) Qualcuno può proporlo?

Grazie!


in primo luogo, quando si imposta found = true; quindi rompi semplicemente; o esci dal giro
jsist

stackoverflow.com/questions/5187888/… . Inoltre, per una ricerca rapida, prova a utilizzare la ricerca binaria e modifica il tuo DS per adattarlo alla situazione ...
jsist

condividono un genitore comune oltre a Object?
Woot4Moo

@ Woot4Moo no, non lo fanno
Ned

Risposte:


219

Se hai solo bisogno di testare l'uguaglianza di base, questo può essere fatto con il JDK di base senza modificare gli elenchi di input su una riga

!Collections.disjoint(list1, list2);

Se devi testare una proprietà specifica, è più difficile. Suggerirei, per impostazione predefinita,

list1.stream()
   .map(Object1::getProperty)
   .anyMatch(
     list2.stream()
       .map(Object2::getProperty)
       .collect(toSet())
       ::contains)

... che raccoglie i valori distinti list2e verifica la presenza di ciascun valore list1.


1
Questo non restituirà sempre falso poiché entrambi sono 2 oggetti diversi?
Venki

2
Ehm, no? Verifica disgiunta se non ci sono oggetti uguali () tra loro tra le due raccolte.
Louis Wasserman

13
Inoltre, tieni presente che, per gli elenchi, questo sarà O (n * m); se sei disposto a copiare list1in a Setprima di confrontare, otterrai O (n) + O (m), cioè O (n + m), al costo di un po 'di RAM extra; si tratta di scegliere tra velocità o memoria.
Haroldo_OK

Questo funzionerebbe solo se "List <Person> list1; List <Person> list2" ma non su due diversi oggetti o tipi di dati come List <Person> list1; List <Employee> lista2.
whoami,

Ovviamente questo non funzionerà per quegli scenari @Zephyr, per la domanda posta, funziona perfettamente fintanto che hai implementato gli uguali giusti. questo è tutto ciò che conta!
Syed Siraj Uddin

38

Puoi usare Apache Commons CollectionUtils :

if(CollectionUtils.containsAny(list1,list2)) {  
    // do whatever you want
} else { 
    // do other thing 
}  

Ciò presuppone che tu abbia correttamente sovraccaricato la funzionalità uguale per i tuoi oggetti personalizzati.


9
sono passati 4 anni e ho anche chiamato esplicitamente il pacchetto e la funzione.
Woot4Moo

1
Downvote per apache commons quando esiste una soluzione solo jdk
ohcibi

9
@ohcibi Java ha anche un logger integrato, dovresti votare le persone che suggeriscono di usare Log4j e Log4j2 mentre ci sei.
Woot4Moo

1
@ Woot4Moo dipende. Non c'è motivo di downvote se c'è un motivo per usare Log4j per risolvere il problema degli OP. In questo caso gli apache commons sarebbero semplicemente inutili come nel 99% delle risposte che suggeriscono gli apache commons.
ohcibi

1
@ohcibi ma se stai già usando Apache commons, allora non è davvero gonfio. Questa è stata una buona risposta.
vab2048

19

Per abbreviare la logica di Narendra, puoi usare questo:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));

3
questa risposta è sottovalutata.
Kervvv

Puoi accorciarlo ancora un po 'se vuoi:list1.stream().anyMatch(list2::contains);
tarka

9

C'è un metodo di Collectiondenominazione retainAllma con alcuni effetti collaterali per il tuo riferimento

Conserva solo gli elementi in questo elenco contenuti nella raccolta specificata (operazione facoltativa). In altre parole, rimuove da questo elenco tutti i suoi elementi che non sono contenuti nella raccolta specificata.

true se questo elenco è cambiato a seguito della chiamata

È come

boolean b = list1.retainAll(list2);

5

La risposta di Loius è corretta, voglio solo aggiungere un esempio:

listOne.add("A");
listOne.add("B");
listOne.add("C");

listTwo.add("D");
listTwo.add("E");
listTwo.add("F");      

boolean noElementsInCommon = Collections.disjoint(listOne, listTwo); // true

1
Penso che se aggiungi l'elemento 'A' alla seconda lista listTwo.add ("A"); anche se Collections.disjoint (listOne, listTwo); restituisce true.
Sairam Kukadala

2

modo più veloce richiederà spazio aggiuntivo.

Per esempio:

  1. metti tutti gli elementi in un elenco in un HashSet (devi implementare la funzione hash da solo per usare object.getAttributeSame ())

  2. Scorri l'altro elenco e controlla se qualche elemento è presente nell'HashSet.

In questo modo ogni oggetto viene visitato al massimo una volta. e HashSet è abbastanza veloce da controllare o inserire qualsiasi oggetto in O (1).


2

Secondo JavaDoc per .contains(Object obj):

Restituisce vero se questo elenco contiene l'elemento specificato. Più formalmente, restituisce true se e solo se questa lista contiene almeno un elemento e tale che (o == null? E == null: o.equals (e)).

Quindi, se sovrascrivi il tuo .equals()metodo per l'oggetto dato, dovresti essere in grado di fare:if(list1.contains(object2))...

Se gli elementi saranno unici (es. Avere attributi diversi) si poteva ignorare il .equals()ed .hashcode()e conservare tutto in HashSets. Questo ti permetterà di verificare se uno contiene un altro elemento in tempo costante.


2

per renderlo più veloce, puoi aggiungere una pausa; in questo modo il ciclo si fermerà se found è impostato su true:

boolean found = false;
for(Object1 object1 : list1){
   for(Object2 object2: list2){
       if(object1.getAttributeSame() == object2.getAttributeSame()){
           found = true;
           //also do something  
           break;
       }
    }
    if(!found){
        //do something
    }
    found = false;
}

Se avessi mappe al posto di elenchi con come chiavi l'attributoSame, potresti controllare più velocemente un valore in una mappa se c'è un valore corrispondente nella seconda mappa o meno.


Ciao Tom, grazie per averlo notato! Sì, ho dimenticato "interruzione" durante la digitazione. Ma stavo pensando che forse c'è qualche algoritmo, o dovrei cambiare questi elenchi in altre raccolte.
Ned

non c'è niente di meglio di O (n * m)?
Woot4Moo

.getAttributeSame ()?
Maveň ツ

L'implementazione per il metodo getAttributeSame () da Object1 e Object2 non viene fornita, ma non è nemmeno rilevante per la domanda e la risposta; restituisce solo un attributo (attributeSame, a Long) che hanno entrambe le classi.
Tom

0

Puoi definire il tipo di dati che conservi? sono big data? è ordinato? Penso che sia necessario considerare diversi approcci di efficienza a seconda dei dati.

Ad esempio, se i tuoi dati sono grandi e non ordinati, potresti provare a iterare i due elenchi insieme per indice e memorizzare ogni attributo di elenco in un altro helper di elenco. quindi puoi effettuare un controllo incrociato in base agli attributi correnti negli elenchi degli helper.

in bocca al lupo

modificato: e non consiglierei di sovraccaricare equals. è pericoloso e probabilmente contro il significato dell'oggetto oop.



0

Con java 8, possiamo fare come di seguito per verificare se un elenco contiene qualsiasi elemento di un altro elenco

boolean var = lis1.stream().filter(element -> list2.contains(element)).findFirst().isPresent();

0

Se vuoi controllare se un elemento esiste in un elenco, usa il metodo contiene.

if (list1.contains(Object o))
{
   //do this
}
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.