2010-03-08 16 views
47

domanda è semplice:Intersezione efficiente di due liste <String> in Java?

Ho due Lista

List<String> columnsOld = DBUtils.GetColumns(db, TableName); 
List<String> columnsNew = DBUtils.GetColumns(db, TableName); 

E ho bisogno di ottenere l'intersezione di questi. C'è un modo rapido per raggiungere questo obiettivo?

+0

perché non basta usare nidificato per-loop? o un singolo ciclo – Ungeheuer

+1

@JohnnyCoder sul serio? – Pentium10

+0

funziona no? Vuoi trovare due elementi che corrispondono, in questo modo funziona. il metodo di conservazione probabilmente fa la stessa cosa, o simile, non lo vedi. – Ungeheuer

risposta

96

È possibile utilizzare retainAll metodo:

columnsOld.retainAll (columnsNew); 
+8

Nota: affinché funzioni con altri oggetti oltre a 'String', è necessario ovviamente implementare' equals' e 'hashCode'. –

17

Dal retainAll non toccherà la raccolta argomento, questo sarebbe più veloce:

List<String> columnsOld = DBUtils.GetColumns(db, TableName); 
List<String> columnsNew = DBUtils.GetColumns(db, TableName); 

for(int i = columnsNew.size() - 1; i > -1; --i){ 
    String str = columnsNew.get(i); 
    if(!columnsOld.remove(str)) 
     columnsNew.remove(str); 
} 

L'intersezione sarà i valori lasciati in columnsNew. La rimozione di valori già confrontati da colonneOld ridurrà il numero di confronti necessari.

+0

Ma il tuo codice dovrebbe essere definitivamente estratto in un nuovo metodo separato perché non è assolutamente chiaro da questo codice cosa fa. Inoltre, non avrei rifiutato un test di unità aggiuntivo per questo codice. – Roman

+0

D'accordo, buon metodo di separazione, denominazione e unit test è sempre la regola numero uno. – bjornhol

+0

Non dovrebbe questo metodo aggiungere gli elementi che non possono essere trovati nelle colonneOld alle colonneNuovo? Sembra che questi elementi manchino nel risultato. – Calon

6

Come su

private List<String> intersect(List<String> A, List<String> B) { 
    List<String> rtnList = new LinkedList<>(); 
    for(String dto : A) { 
     if(B.contains(dto)) { 
      rtnList.add(dto); 
     } 
    } 
    return rtnList; 
} 
+0

Questo non ti darà il risultato corretto in tutti i casi. Se B contiene elementi che non sono contenuti in A, il tuo metodo manca quegli elementi. – Calon

+6

Se B contiene elementi che non sono contenuti in A, non è necessario iterare su quegli elementi perché stiamo cercando di trovare tutti gli elementi sia in A che in B. – juan2raid

1

C'è un bel modo con i flussi che può fare questo in una sola riga di codice e si può due liste che non sono dello stesso tipo che non è possibile con il metodo contieneAll: o

columnsOld.stream().filter(c -> columnsNew.contains(c)).collect(Collectors.toList()); 

Un esempio per elenchi con tipi diversi. Se si dispone di un realtion tra foo e bar e si può ottenere un bar-oggetto da foo di quanto è possibile modificare il vostro flusso:

List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo())); 
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar())); 

fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList()); 
+0

'c -> columnsNew.contains (c)' lambda può essere riscritto più conciso come riferimento al metodo: 'columnsNew :: contains'. – Bass

+0

non funzionerà nel tempo O (n^2)? –

0

Se si mette il secondo elenco in un set dire HashSet. E basta scorrere il primo elenco per verificare la presenza sul set e rimuoverlo se non è presente, il tuo primo elenco alla fine avrà l'intersezione di cui hai bisogno. Sarà molto più veloce di retainAll o contiene in una lista. L'enfasi qui è di usare un insieme invece di una lista. Le ricerche sono O (1). firstList.retainAll (nuovo HashSet (secondList)) funzionerà anche.

0

utilizzando retainAll se non si preoccupano occorrenze, altrimenti utilizzando N.intersection

a = N.asList(12, 16, 16, 17, 19); 
b = N.asList(16, 19, 107); 
a.retainAll(b); // [16, 16, 19] 
N.println(a); 

a = N.asList(12, 16, 16, 17, 19); 
b = N.asList(16, 19, 107); 
a = N.intersect(a, b); 
N.println(a); // [16, 19] 

N è una classe di utilità in AbacusUtil