Compruebe si una lista contiene elementos de la otra

105

Tengo dos listas con diferentes objetos en ellas.

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

Quiero verificar si el elemento de list1 existe en list2, según un atributo específico (Object1 y Object2 tienen (entre otros), un atributo mutuo (con tipo Long), llamado attributeSame).

ahora mismo, lo hago así:

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

Pero creo que hay una manera mejor y más rápida de hacer esto :) ¿Alguien puede proponerlo?

¡Gracias!

Ned
fuente
en primer lugar, cuando establece found = true; luego simplemente rompa; o salir del circuito
jsist
stackoverflow.com/questions/5187888/… . Por otra parte, para la búsqueda rápida intente utilizar búsqueda binaria y cambiar su DS a la habitación de la situación ...
jsist
¿comparten un padre común además de Object?
Woot4Moo
@ Woot4Moo no, no lo hacen
Ned

Respuestas:

219

Si solo necesita probar la igualdad básica, esto se puede hacer con el JDK básico sin modificar las listas de entrada en una línea

!Collections.disjoint(list1, list2);

Si necesita probar una propiedad específica, es más difícil. Recomendaría, por defecto,

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

... que recopila los distintos valores list2y prueba la list1presencia de cada valor .

Louis Wasserman
fuente
1
¿No devolverá siempre falso ya que ambos son 2 objetos diferentes?
Venki
2
¿Mmm no? Pruebas disjuntas si no hay objetos iguales () entre sí entre las dos colecciones.
Louis Wasserman
13
Además, tenga en cuenta que, para las listas, será O (n * m); si está dispuesto a copiar list1en a Setantes de comparar, obtendrá O (n) + O (m), es decir, O (n + m), a costa de algo de RAM adicional; es cuestión de elegir entre velocidad o memoria.
Haroldo_OK
Esto funcionaría sólo si "Lista <Persona> lista1; Lista <Persona> lista2" pero no en dos objetos o tipos de datos diferentes como Lista <Persona> list1; Lista <Empleado> list2.
whoami
Por supuesto, esto no funcionará para esos escenarios @Zephyr, para la pregunta formulada, funciona perfectamente siempre que tenga implementados los iguales correctos. eso es todo!
Syed Siraj Uddin
38

Puede utilizar Apache Commons CollectionUtils :

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

Esto supone que ha sobrecargado correctamente la funcionalidad de iguales para sus objetos personalizados.

Woot4Moo
fuente
9
Han pasado 4 años y también llamo explícitamente el paquete y la función.
Woot4Moo
1
Downvote para Apache bien común, cuando hay una única solución JDK
ohcibi
9
@ohcibi Java también tiene un registrador incorporado, debe votar en contra de las personas que sugieren usar Log4j y Log4j2 mientras lo hace.
Woot4Moo
1
@ Woot4Moo, depende. No hay razón para rechazar la votación si hay una razón para usar Log4j para resolver el problema de los OP. En este caso, los comunes de apache serían simplemente inútiles como en el 99% de las respuestas que sugieren los comunes de apache.
ohcibi
1
@ohcibi, pero si ya está usando Apache commons, entonces no es realmente grande. Esta fue una buena respuesta.
vab2048
19

Para acortar la lógica de Narendra, puede usar esto:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
ketanjaína
fuente
3
esta respuesta es subestimada.
Kervvv
Puedes acortarlo un poco más si quieres:list1.stream().anyMatch(list2::contains);
tarka
9

Hay un método para Collectionnombrar retainAllpero que tiene algunos efectos secundarios para su referencia

Conserva solo los elementos de esta lista que están contenidos en la colección especificada (operación opcional). En otras palabras, elimina de esta lista todos sus elementos que no están contenidos en la colección especificada.

Verdadero si esta lista cambió como resultado de la llamada

Es como

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

La respuesta de Loius es correcta, solo quiero agregar un ejemplo:

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
fuente
1
Creo que si agrega el elemento 'A' a la segunda lista listTwo.add ("A"); aunque Collections.disjoint (listOne, listTwo); devuelve verdadero.
Sairam Kukadala
2

forma más rápida requerirá espacio adicional.

Por ejemplo:

  1. ponga todos los elementos en una lista en un HashSet (debe implementar la función hash usted mismo para usar object.getAttributeSame ())

  2. Revise la otra lista y verifique si algún elemento está en el HashSet.

De esta forma, cada objeto se visita como máximo una vez. y HashSet es lo suficientemente rápido para verificar o insertar cualquier objeto en O (1).

lavin
fuente
2

Según JavaDoc para .contains(Object obj):

Devuelve verdadero si esta lista contiene el elemento especificado. Más formalmente, devuelve verdadero si y solo si esta lista contiene al menos un elemento e tal que (o == null? E == null: o.equals (e)).

Entonces, si anula su .equals()método para su objeto dado, debería poder hacer:if(list1.contains(object2))...

Si los elementos serán únicos (es decir. Tener diferentes atributos) que podría invalidar la .equals()e .hashcode()y almacenar todo en HashSets. Esto le permitirá comprobar si uno contiene otro elemento en tiempo constante.

npinti
fuente
2

para hacerlo más rápido, puede agregar un descanso; de esa manera, el bucle se detendrá si se encuentra establecido en verdadero:

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 tuviera mapas en lugar de listas con como claves el atributo Igual, podría verificar más rápido un valor en un mapa si hay un valor correspondiente en el segundo mapa o no.

Tom
fuente
Hola Tom, ¡gracias por darte cuenta! Sí, olvidé "romper" mientras escribía. Pero estaba pensando que tal vez haya algún algoritmo, o debería cambiar estas listas por otras colecciones.
Ned
¿No hay nada mejor que O (n * m)?
Woot4Moo
.getAttributeSame ()?
Maveň ツ
No se proporciona la implementación para el método getAttributeSame () de Object1 y Object2, pero tampoco son relevantes para la pregunta y la respuesta; simplemente devuelve un atributo (attributeSame, un Long) que tienen ambas clases.
Tom
0

¿Puede definir el tipo de datos que tiene? ¿es big data? esta ordenado? Creo que debe considerar diferentes enfoques de eficiencia en función de los datos.

Por ejemplo, si sus datos son grandes y no están clasificados, puede intentar iterar las dos listas juntas por índice y almacenar cada atributo de lista en otro asistente de lista. luego podría verificar los atributos actuales en las listas de ayuda.

buena suerte

editado: y no recomendaría sobrecargar iguales. es peligroso y probablemente en contra de su objeto oop significado.

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

Con java 8, podemos hacer lo siguiente para verificar si una lista contiene algún elemento de otra lista

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

Si desea verificar si un elemento existe en una lista, use el método contiene.

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