Sprawdź, czy jedna lista zawiera element z drugiej

108

Mam dwie listy z różnymi obiektami.

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

Chcę sprawdzić, czy element z listy1 istnieje na liście2, na podstawie określonego atrybutu (Object1 i Object2 mają (między innymi) jeden wspólny atrybut (z typem Long) o nazwie attributeSame).

teraz robię to tak:

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;
}

Ale myślę, że jest na to lepszy i szybszy sposób :) Czy ktoś może to zaproponować?

Dzięki!

Ned
źródło
po pierwsze, kiedy ustawisz found = true; następnie po prostu złam; lub wyjdź z pętli
jsist
stackoverflow.com/questions/5187888/… . Co więcej, w celu szybkiego wyszukiwania spróbuj użyć wyszukiwania binarnego i zmień DS, aby dostosować się do sytuacji ...
jsist
czy mają wspólnego rodzica poza Object?
Woot4Moo
@ Woot4Moo nie, nie robią
Ned

Odpowiedzi:

226

Jeśli potrzebujesz tylko przetestować podstawową równość, można to zrobić za pomocą podstawowego JDK bez modyfikowania list wejściowych w jednej linii

!Collections.disjoint(list1, list2);

Jeśli chcesz przetestować określoną właściwość, jest to trudniejsze. Poleciłbym domyślnie

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

... który zbiera różne wartości list2wi testuje każdą wartość pod list1kątem obecności.

Louis Wasserman
źródło
1
Czy to nie zawsze zwróci fałsz, ponieważ oba są dwoma różnymi obiektami?
Venki,
2
Yyy ... nie? Testy rozłączne, jeśli między dwiema kolekcjami nie ma obiektów równych ().
Louis Wasserman,
13
Należy również pamiętać, że w przypadku list będzie to O (n * m); jeśli chcesz skopiować list1do a Setprzed porównaniem, otrzymasz O (n) + O (m), to znaczy O (n + m), kosztem dodatkowej pamięci RAM; to kwestia wyboru między szybkością a pamięcią.
Haroldo_OK
To zadziała tylko wtedy, gdy „List <Osoba> lista1; Lista <Osoba> lista2”, ale nie na dwóch różnych obiektach lub typach danych, takich jak Lista <Osoba> lista1; Lista <Pracownik> lista 2.
whoami
Oczywiście to nie zadziała w tych scenariuszach @Zephyr, dla zadanego pytania działa idealnie, o ile masz wdrożonych odpowiednich równych sobie. to wszystko się liczy!
Syed Siraj Uddin
38

Możesz użyć Apache Commons CollectionUtils :

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

Zakłada się, że poprawnie przeciążono funkcjonalność equals dla obiektów niestandardowych.

Woot4Moo
źródło
9
minęły już 4 lata, a ja również wyraźnie opisuję pakiet i funkcję.
Woot4Moo
1
Głos przeciw apache commons, gdy istnieje rozwiązanie tylko dla
JDK
9
@ohcibi Java ma również wbudowany rejestrator, powinieneś zagłosować w dół osoby, które sugerują używanie Log4j i Log4j2, gdy jesteś przy tym.
Woot4Moo
1
@ Woot4Moo to zależy. Nie ma powodu, aby głosować przeciw, jeśli istnieje powód, aby użyć Log4j do rozwiązania problemu PO. W tym przypadku apache commons byłoby po prostu bezużytecznym wzdęciem, jak w 99% odpowiedzi sugerujących apache commons.
ohcibi
1
@ohcibi, ale jeśli już używasz Apache commons, to nie jest to naprawdę nadęty. To była dobra odpowiedź.
vab2048
20

Aby skrócić logikę Narendry, możesz użyć tego:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
ketanjain
źródło
3
ta odpowiedź jest niedoceniana.
Kervvv
1
Jeśli chcesz, możesz trochę skrócić:list1.stream().anyMatch(list2::contains);
tarka
9

Jest jedna metoda o Collectionnazwie retainAll, ale o pewne skutki uboczne dla Ciebie odniesienia

Zachowuje tylko elementy z tej listy, które znajdują się w określonej kolekcji (operacja opcjonalna). Innymi słowy, usuwa z tej listy wszystkie jej elementy, które nie są zawarte w określonej kolekcji.

prawda, jeśli ta lista uległa zmianie w wyniku połączenia

To jest jak

boolean b = list1.retainAll(list2);
Harmeet Singh
źródło
5

Odpowiedź Loiusa jest poprawna, chcę tylko dodać przykład:

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
Matias Elorriaga
źródło
1
Myślę, że jeśli dodasz element 'A' do drugiej listy listTwo.add ("A"); mimo że Collections.disjoint (listOne, listTwo); zwraca prawdę.
Sairam Kukadala
2

szybszy sposób będzie wymagał dodatkowej przestrzeni.

Na przykład:

  1. umieść wszystkie pozycje z jednej listy w HashSet (musisz samodzielnie zaimplementować funkcję hash, aby użyć object.getAttributeSame ())

  2. Przejrzyj drugą listę i sprawdź, czy jakikolwiek element znajduje się w HashSet.

W ten sposób każdy obiekt odwiedzany jest najwyżej raz. a HashSet jest wystarczająco szybki, aby sprawdzić lub wstawić dowolny obiekt do O (1).

lavin
źródło
2

Według JavaDoc dla .contains(Object obj):

Zwraca wartość true, jeśli ta lista zawiera określony element. Bardziej formalnie, zwraca prawdę wtedy i tylko wtedy, gdy ta lista zawiera przynajmniej jeden element e taki, że (o == null? E == null: o.equals (e)).

Więc jeśli nadpisujesz swoją .equals()metodę dla danego obiektu, powinieneś być w stanie zrobić:if(list1.contains(object2))...

Jeśli elementy będzie wyjątkowy (tzn. Mają różne atrybuty) można zastąpić .equals()i .hashcode()i przechowywać wszystko HashSets. Pozwoli ci to sprawdzić, czy jeden zawiera inny element w stałym czasie.

npinti
źródło
2

aby przyspieszyć, możesz dodać przerwę; w ten sposób pętla zatrzyma się, jeśli found ma wartość 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;
}

Gdybyś miał mapy zamiast list z kluczami o nazwie Atrybut ten sam, możesz szybciej sprawdzić wartość na jednej mapie, czy na drugiej mapie jest odpowiednia wartość, czy nie.

Tomek
źródło
Cześć Tom, dzięki, że zauważyłeś! Tak, zapomniałem o „przerwie” podczas pisania. Ale myślałem, że może jest jakiś algorytm lub powinienem zmienić te listy w inne kolekcje.
Ned,
czy nie ma nic lepszego niż O (n * m)?
Woot4Moo
.getAttributeSame ()?
Maveň ツ
Nie podano implementacji metody getAttributeSame () z Object1 i Object2, ale nie są one również istotne dla pytania i odpowiedzi; po prostu zwraca atrybut (attributeSame, a Long), który mają obie klasy.
Tom
0

Czy możesz zdefiniować rodzaj przechowywanych danych? czy to duże zbiory danych? czy to jest posortowane? Myślę, że w zależności od danych należy rozważyć różne podejścia do wydajności.

Na przykład, jeśli twoje dane są duże i nieposortowane, możesz spróbować iterować dwie listy razem według indeksu i przechowywać każdy atrybut listy w innym pomocniku list. następnie możesz sprawdzić bieżące atrybuty na listach pomocników.

powodzenia

redagowane: i nie polecałbym przeciążania równa się. jest to niebezpieczne i prawdopodobnie przeciwko twojemu przedmiotowi oop znaczenie.

REL
źródło
0

org.springframework.util.CollectionUtils

boolean containsAny(java.util.Collection<?> source, java.util.Collection<?> candidates)

Return true if any element in 'candidates' is contained in 'source'; otherwise returns false
akjain
źródło
0

Za pomocą java 8możemy zrobić jak poniżej, aby sprawdzić, czy jedna lista zawiera jakikolwiek element innej listy

boolean var = lis1.stream().filter(element -> list2.contains(element)).findFirst().isPresent();
Narendra Jaggi
źródło
0

Jeśli chcesz sprawdzić, czy element istnieje na liście, użyj metody zawiera.

if (list1.contains(Object o))
{
   //do this
}
Fakipo
źródło