Überprüfen Sie, ob eine Liste Elemente aus der anderen enthält

104

Ich habe zwei Listen mit unterschiedlichen Objekten.

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

Ich möchte überprüfen, ob ein Element aus Liste1 in Liste2 vorhanden ist, basierend auf einem bestimmten Attribut (Objekt1 und Objekt2 haben (unter anderem) ein gemeinsames Attribut (mit dem Typ Long) mit dem Namen attributeSame).

im Moment mache ich es so:

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

Aber ich denke, es gibt einen besseren und schnelleren Weg, dies zu tun :) Kann jemand es vorschlagen?

Vielen Dank!

Ned
quelle
Erstens, wenn Sie found = true setzen. dann einfach brechen; oder kommen Sie aus der Schleife
jsist
stackoverflow.com/questions/5187888/… . Darüber hinaus versuchen Sie für eine schnelle Suche die binäre Suche und ändern Sie Ihren DS, um der Situation zu entsprechen ...
jsist
teilen sie neben Object ein gemeinsames Elternteil?
Woot4Moo
@ Woot4Moo nein, sie nicht
Ned

Antworten:

219

Wenn Sie nur die grundlegende Gleichheit testen müssen, können Sie dies mit dem grundlegenden JDK tun, ohne die Eingabelisten in der einen Zeile zu ändern

!Collections.disjoint(list1, list2);

Wenn Sie eine bestimmte Eigenschaft testen müssen, ist dies schwieriger. Ich würde standardmäßig empfehlen,

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

... die die unterschiedlichen Werte in sammelt list2und jeden Wert list1auf Präsenz prüft .

Louis Wasserman
quelle
1
Wird dies nicht immer false zurückgeben, da beide zwei verschiedene Objekte sind?
Venki
2
Ähm, nein? Disjunkte Tests, wenn zwischen den beiden Sammlungen keine Objekte gleich () vorhanden sind.
Louis Wasserman
13
Beachten Sie außerdem, dass dies für Listen O (n * m) ist. Wenn Sie bereit sind, vor dem Vergleich list1in a zu kopieren Set, erhalten Sie O (n) + O (m), dh O (n + m), auf Kosten eines zusätzlichen Arbeitsspeichers. Es ist eine Frage der Wahl zwischen Geschwindigkeit oder Speicher.
Haroldo_OK
Dies würde nur funktionieren, wenn "List <Person> list1; List <Person> list2", jedoch nicht für zwei verschiedene Objekte oder Datentypen wie List <Person> list1; Liste <Mitarbeiter> Liste2.
Whoami
Natürlich funktioniert dies nicht für diese Szenarien @Zephyr. Für die gestellte Frage funktioniert es perfekt, solange Sie die richtigen Gleichen implementiert haben. das ist alles wichtig!
Syed Siraj Uddin
38

Sie können Apache Commons CollectionUtils verwenden :

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

Dies setzt voraus, dass Sie die Gleichheitsfunktionalität für Ihre benutzerdefinierten Objekte ordnungsgemäß überladen haben.

Woot4Moo
quelle
9
Es ist 4 Jahre her und ich rufe auch ausdrücklich das Paket und die Funktion auf.
Woot4Moo
1
Downvote für Apache Commons, wenn es nur eine JDK-Lösung gibt
Ohcibi
9
@ohcibi Java hat auch einen eingebauten Logger. Sie sollten Leute ablehnen, die vorschlagen, Log4j und Log4j2 zu verwenden, während Sie gerade dabei sind.
Woot4Moo
1
@ Woot4Moo es kommt darauf an. Es gibt keinen Grund zur Ablehnung, wenn es einen Grund gibt, Log4j zur Lösung des OP-Problems zu verwenden. In diesem Fall wäre Apache Commons einfach nutzlos, wie in 99% der Antworten, die Apache Commons vorschlagen.
Ohcibi
1
@ohcibi aber wenn Sie bereits Apache Commons verwenden, dann ist es nicht wirklich aufgebläht. Das war eine gute Antwort.
Vab2048
18

Um Narendras Logik zu verkürzen, können Sie Folgendes verwenden:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
Ketanjain
quelle
3
Diese Antwort wird unterschätzt.
Kervvv
Sie können es etwas list1.stream().anyMatch(list2::contains);
kürzer machen,
9

Es ist eine Methode von dem CollectionNamen , retainAllaber mit einigen Nebenwirkungen für Sie Referenz

Behält nur die Elemente in dieser Liste bei, die in der angegebenen Sammlung enthalten sind (optionaler Vorgang). Mit anderen Worten, werden alle Elemente aus dieser Liste entfernt, die nicht in der angegebenen Sammlung enthalten sind.

true, wenn sich diese Liste infolge des Aufrufs geändert hat

Es ist wie

boolean b = list1.retainAll(list2);
Harmeet Singh
quelle
5

Loius Antwort ist richtig, ich möchte nur ein Beispiel hinzufügen:

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
quelle
1
Ich denke, wenn Sie Element 'A' zur zweiten Liste hinzufügen listTwo.add ("A"); obwohl Collections.disjoint (listOne, listTwo); gibt true zurück.
Sairam Kukadala
2

Ein schnellerer Weg erfordert zusätzlichen Platz.

Beispielsweise:

  1. Fügen Sie alle Elemente in einer Liste in ein HashSet ein (Sie müssen die Hash-Funktion selbst implementieren, um object.getAttributeSame () verwenden zu können.)

  2. Gehen Sie die andere Liste durch und prüfen Sie, ob sich ein Element im HashSet befindet.

Auf diese Weise wird jedes Objekt höchstens einmal besucht. und HashSet ist schnell genug, um ein Objekt in O (1) zu überprüfen oder einzufügen.

Lavin
quelle
2

Laut JavaDoc für die .contains(Object obj):

Gibt true zurück, wenn diese Liste das angegebene Element enthält. Genauer gesagt, gibt true nur dann zurück, wenn diese Liste mindestens ein Element e enthält, so dass (o == null? E == null: o.equals (e)).

Wenn Sie also Ihre .equals()Methode für Ihr bestimmtes Objekt überschreiben , sollten Sie in der Lage sein:if(list1.contains(object2))...

Wenn die Elemente eindeutig sind (dh unterschiedliche Attribute haben), können Sie das .equals()und überschreiben .hashcode()und alles in speichern HashSets. Auf diese Weise können Sie in konstanter Zeit überprüfen, ob eines ein anderes Element enthält.

npinti
quelle
2

Um es schneller zu machen, können Sie eine Pause hinzufügen. Auf diese Weise stoppt die Schleife, wenn gefunden auf true gesetzt ist:

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

Wenn Sie Karten anstelle von Listen mit den Schlüsseln attributeSame als Schlüssel haben würden, könnten Sie schneller nach einem Wert in einer Karte suchen, ob die zweite Karte einen entsprechenden Wert enthält oder nicht.

Tom
quelle
Hallo Tom, danke, dass du es bemerkt hast! Ja, ich habe "break" beim Tippen vergessen. Aber ich dachte, vielleicht gibt es einen Algorithmus, oder ich sollte diese Listen in andere Sammlungen ändern.
Ned
Gibt es nichts Besseres als O (n * m)?
Woot4Moo
.getAttributeSame ()?
Maveň ツ
Die Implementierung für die Methode getAttributeSame () von Object1 und Object2 wird nicht bereitgestellt, ist aber auch für die Frage und Antwort nicht relevant. Es wird nur ein Attribut (attributeSame, a Long) zurückgegeben, das beide Klassen haben.
Tom
0

Können Sie die Art der Daten definieren, die Sie speichern? ist es Big Data? ist es sortiert? Ich denke, dass Sie je nach Daten unterschiedliche Effizienzansätze berücksichtigen müssen.

Wenn Ihre Daten beispielsweise groß und unsortiert sind, können Sie versuchen, die beiden Listen zusammen nach Index zu iterieren und jedes Listenattribut in einem anderen Listenhelfer zu speichern. Dann können Sie die aktuellen Attribute in den Hilfslisten überprüfen.

Viel Glück

bearbeitet: und ich würde nicht empfehlen, gleich viel zu überladen. Es ist gefährlich und wahrscheinlich gegen Ihr Objekt.

REL
quelle
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
quelle
0

Mit java 8können wir wie folgt prüfen, ob eine Liste ein Element einer anderen Liste enthält

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

Wenn Sie überprüfen möchten, ob ein Element in einer Liste vorhanden ist, verwenden Sie die Methode includes.

if (list1.contains(Object o))
{
   //do this
}
Fakipo
quelle