Vérifiez si une liste contient un élément de l'autre

105

J'ai deux listes avec des objets différents.

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

Je veux vérifier si l'élément de list1 existe dans list2, basé sur un attribut spécifique (Object1 et Object2 ont (entre autres), un attribut mutuel (de type Long), nommé attributeSame).

maintenant, je le fais comme ceci:

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

Mais je pense qu'il existe un moyen meilleur et plus rapide de le faire :) Quelqu'un peut-il le proposer?

Merci!

Ned
la source
tout d'abord, lorsque vous définissez found = true; puis simplement casser; ou sortez de la boucle
jsist
stackoverflow.com/questions/5187888/… . De plus, pour une recherche rapide, essayez d'utiliser la recherche binaire et changez votre DS en fonction de la situation ...
jsist
partagent-ils un parent commun en plus d'Object?
Woot4Moo
@ Woot4Moo non, ils ne le font pas
Ned

Réponses:

221

Si vous avez juste besoin de tester l'égalité de base, cela peut être fait avec le JDK de base sans modifier les listes d'entrée sur une seule ligne

!Collections.disjoint(list1, list2);

Si vous avez besoin de tester une propriété spécifique, c'est plus difficile. Je recommanderais, par défaut,

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

... qui recueille les valeurs distinctes list2et teste chaque valeur list1pour la présence.

Louis Wasserman
la source
1
Cela ne retournera-t-il pas toujours faux puisque les deux sont 2 objets différents?
Venki
2
Um non? Teste disjoint s'il n'y a pas d'objets égaux () l'un à l'autre entre les deux collections.
Louis Wasserman
13
Veuillez également noter que, pour les listes, ce sera O (n * m); si vous êtes prêt à copier list1dans un Setavant de comparer, vous obtiendrez O (n) + O (m), c'est-à-dire O (n + m), au prix d'un peu de RAM supplémentaire; il s'agit de choisir entre la vitesse ou la mémoire.
Haroldo_OK
Cela ne fonctionnerait que si "List <Person> list1; List <Person> list2" mais pas sur deux objets ou types de données différents comme List <Person> list1; Liste <Employee> list2.
whoami du
Bien sûr, cela ne fonctionnera pas pour ces scénarios @Zephyr, pour la question posée, cela fonctionne parfaitement tant que vous avez implémenté les bons égaux. c'est tout ce qui compte!
Syed Siraj Uddin
38

Vous pouvez utiliser Apache Commons CollectionUtils :

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

Cela suppose que vous avez correctement surchargé la fonctionnalité d'égalité pour vos objets personnalisés.

Woot4Moo
la source
9
cela fait 4 ans et j'appelle aussi explicitement le package et la fonction.
Woot4Moo
1
Downvote pour Apache Commons quand il y a une solution
JDK Only
9
@ohcibi Java a également un enregistreur intégré, vous devriez voter pour les personnes qui suggèrent d'utiliser Log4j et Log4j2 pendant que vous y êtes.
Woot4Moo
1
@ Woot4Moo cela dépend. Il n'y a aucune raison de voter contre s'il y a une raison d'utiliser Log4j pour résoudre le problème des OP. Dans ce cas, apache commons serait simplement un gonflement inutile, comme dans 99% des réponses suggérant apache commons.
ohcibi
1
@ohcibi mais si vous utilisez déjà Apache commons, ce n'est pas vraiment un gonflement. C'était une bonne réponse.
vab2048
20

Pour raccourcir la logique de Narendra, vous pouvez utiliser ceci:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
ketanjain
la source
3
cette réponse est sous-estimée.
Kervvv
1
Vous pouvez le raccourcir un peu plus si vous le souhaitez:list1.stream().anyMatch(list2::contains);
tarka
9

Il existe une méthode de Collectionnommer retainAllmais ayant des effets secondaires pour vous référence

Conserve uniquement les éléments de cette liste qui sont contenus dans la collection spécifiée (opération facultative). En d'autres termes, supprime de cette liste tous ses éléments qui ne sont pas contenus dans la collection spécifiée.

true si cette liste a changé suite à l'appel

C'est comme

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

La réponse de Loius est correcte, je veux juste ajouter un exemple:

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
la source
1
Je pense que si vous ajoutez l'élément 'A' à la deuxième liste listTwo.add ("A"); même si Collections.disjoint (listOne, listTwo); renvoie vrai.
Sairam Kukadala
2

plus rapide nécessitera un espace supplémentaire.

Par exemple:

  1. mettre tous les éléments dans une liste dans un HashSet (vous devez implémenter la fonction de hachage par vous-même pour utiliser object.getAttributeSame ())

  2. Parcourez l'autre liste et vérifiez si un élément se trouve dans le HashSet.

De cette manière, chaque objet est visité au plus une fois. et HashSet est assez rapide pour vérifier ou insérer n'importe quel objet dans O (1).

Lavin
la source
2

Selon le JavaDoc pour .contains(Object obj):

Renvoie true si cette liste contient l'élément spécifié. Plus formellement, renvoie true si et seulement si cette liste contient au moins un élément e tel que (o == null? E == null: o.equals (e)).

Donc, si vous remplacez votre .equals()méthode pour votre objet donné, vous devriez pouvoir faire:if(list1.contains(object2))...

Si les éléments sont uniques (c'est-à-dire qu'ils ont des attributs différents), vous pouvez remplacer .equals()et .hashcode()et tout stocker dans HashSets. Cela vous permettra de vérifier si l'un contient un autre élément en temps constant.

npinti
la source
2

pour le rendre plus rapide, vous pouvez ajouter une pause; de cette façon, la boucle s'arrêtera si found est défini sur 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;
}

Si vous aviez des cartes au lieu de listes avec comme clés l'attributSame, vous pourriez rechercher plus rapidement une valeur dans une carte s'il existe une valeur correspondante dans la deuxième carte ou non.

À M
la source
Salut Tom, merci d'avoir remarqué! Oui, j'ai oublié "pause" en tapant. Mais je pensais qu'il y avait peut-être un algorithme, ou je devrais changer ces listes en d'autres collections.
Ned
n'y a-t-il rien de mieux que O (n * m)?
Woot4Moo
.getAttributeSame ()?
Maveň ツ
L'implémentation de la méthode getAttributeSame () de Object1 et Object2 n'est pas fournie, mais n'est pas non plus pertinente pour la question et la réponse; il renvoie simplement un attribut (attributeSame, un Long) que possèdent les deux classes.
Tom du
0

Pouvez-vous définir le type de données que vous détenez? est-ce du big data? est-il trié? Je pense que vous devez envisager différentes approches d'efficacité en fonction des données.

Par exemple, si vos données sont volumineuses et non triées, vous pouvez essayer d'itérer les deux listes ensemble par index et stocker chaque attribut de liste dans un autre assistant de liste. alors vous pouvez recouper les attributs actuels dans les listes d'aide.

bonne chance

édité: et je ne recommanderais pas de surcharger les égaux. son dangereux et probablement contre votre sens objet oop.

REL
la source
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
la source
0

Avec java 8, nous pouvons faire comme ci-dessous pour vérifier si une liste contient un élément d'une autre liste

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

Si vous souhaitez vérifier si un élément existe dans une liste, utilisez la méthode contains.

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