Verifique se uma lista contém elementos da outra

105

Tenho duas listas com objetos diferentes.

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

Quero verificar se o elemento da lista1 existe na lista2, com base no atributo específico (Object1 e Object2 têm (entre outros), um atributo mútuo (com tipo Long), denominado attributeSame).

agora, eu faço assim:

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

Mas acho que existe uma maneira melhor e mais rápida de fazer isso :) Alguém pode sugerir?

Obrigado!

Ned
fonte
em primeiro lugar, quando você define found = true; então simplesmente pare; ou saia do circuito
jsist
stackoverflow.com/questions/5187888/… . Além disso, para uma pesquisa rápida, tente usar a Pesquisa Binária e mude seu DS para se
adequar
eles compartilham um pai comum além do objeto?
Woot4Moo
@ Woot4Moo não, eles não fazem
Ned

Respostas:

219

Se você precisa apenas testar a igualdade básica, isso pode ser feito com o JDK básico sem modificar as listas de entrada em uma linha

!Collections.disjoint(list1, list2);

Se você precisa testar uma propriedade específica, isso é mais difícil. Eu recomendaria, por padrão,

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

... que coleta os valores distintos list2e testa cada valor em list1quanto à presença.

Louis Wasserman
fonte
1
Isso não retornará sempre falso, já que ambos são dois objetos diferentes?
Venki
2
Hum, não? Disjoint testa se não há objetos iguais () entre si entre as duas coleções.
Louis Wasserman
13
Além disso, observe que, para listas, será O (n * m); se você deseja copiar list1em a Setantes de comparar, obterá O (n) + O (m), ou seja, O (n + m), ao custo de um pouco de RAM extra; é uma questão de escolher entre velocidade ou memória.
Haroldo_OK
Isso funcionaria apenas se "List <Person> list1; List <Person> list2", mas não em dois objetos ou tipos de dados diferentes como List <Person> list1; List <Employee> list2.
whoami
É claro que isso não funcionará para esses cenários @Zephyr, para a pergunta feita, funciona perfeitamente contanto que você tenha implementados os iguais corretos. isso é tudo que importa!
Syed Siraj Uddin
38

Você pode usar Apache Commons CollectionUtils :

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

Isso pressupõe que você sobrecarregou adequadamente a funcionalidade equals para seus objetos personalizados.

Woot4Moo
fonte
9
já se passaram 4 anos e também chamo explicitamente o pacote e a função.
Woot4Moo de
1
Votar negativamente
9
@ohcibi Java também tem um logger embutido, você deve ir ao downvote pessoas que sugerem o uso de Log4j e Log4j2 enquanto você está nisso.
Woot4Moo
1
@ Woot4Moo isso depende. Não há motivo para downvote se houver um motivo para usar o Log4j para resolver o problema de OPs. Nesse caso, o apache commons seria simplesmente um inchaço inútil, como em 99% das respostas que sugerem o apache commons.
ohcibi
1
@ohcibi, mas se você já está usando o Apache commons, então não é realmente um inchaço. Esta foi uma boa resposta.
vab2048
19

Para encurtar a lógica de Narendra, você pode usar isto:

boolean var = lis1.stream().anyMatch(element -> list2.contains(element));
Ketanjain
fonte
3
esta resposta é pouco apreciada.
Kervvv
Você pode encurtar um pouco mais se quiser:list1.stream().anyMatch(list2::contains);
tarka
9

Existe um método de Collectionnomeação, retainAllmas tendo alguns efeitos colaterais para sua referência

Retém apenas os elementos nesta lista que estão contidos na coleção especificada (operação opcional). Em outras palavras, remove desta lista todos os seus elementos que não estão contidos na coleção especificada.

verdadeiro se esta lista mudou como resultado da chamada

É como

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

A resposta do Loius está correta, só quero adicionar um exemplo:

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
fonte
1
Acho que se você adicionar o elemento 'A' à segunda lista listTwo.add ("A"); mesmo que Collections.disjoint (listOne, listTwo); retorna verdadeiro.
Sairam Kukadala
2

maneira mais rápida exigirá espaço adicional.

Por exemplo:

  1. coloque todos os itens em uma lista em um HashSet (você deve implementar a função hash sozinho para usar object.getAttributeSame ())

  2. Percorra a outra lista e verifique se algum item está no HashSet.

Desta forma, cada objeto é visitado no máximo uma vez. e o HashSet é rápido o suficiente para verificar ou inserir qualquer objeto em O (1).

lavin
fonte
2

De acordo com o JavaDoc para .contains(Object obj):

Retorna verdadeiro se esta lista contém o elemento especificado. Mais formalmente, retorna true se e somente se esta lista contém pelo menos um elemento e tal que (o == null? E == null: o.equals (e)).

Portanto, se você substituir seu .equals()método para o objeto fornecido, deverá ser capaz de fazer:if(list1.contains(object2))...

Se os elementos será único (ie. Tem atributos diferentes), você poderia substituir o .equals()e .hashcode()e armazenar tudo em HashSets. Isso permitirá que você verifique se um contém outro elemento em tempo constante.

npinti
fonte
2

para torná-lo mais rápido, você pode adicionar uma pausa; dessa forma, o loop irá parar se encontrado for definido como verdadeiro:

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

Se você tivesse mapas em vez de listas com como chaves o attributeSame, você poderia verificar mais rapidamente se há um valor em um mapa se há um valor correspondente no segundo mapa ou não.

Tom
fonte
Oi Tom, obrigado por notar! Sim, esqueci o "intervalo" ao digitar. Mas eu estava pensando que talvez haja algum algoritmo, ou devo transformar essas listas em outras coleções.
Ned
não há nada melhor do que O (n * m)?
Woot4Moo
.getAttributeSame ()?
Maveň ツ
A implementação do método getAttributeSame () de Object1 e Object2 não é fornecida, mas também não é relevante para a pergunta e resposta; ele apenas retorna um atributo (attributeSame, um Long) que ambas as classes possuem.
Tom,
0

Você pode definir o tipo de dados que mantém? é big data? está classificado? Acho que você precisa considerar diferentes abordagens de eficiência, dependendo dos dados.

Por exemplo, se seus dados forem grandes e não classificados, você pode tentar iterar as duas listas juntas por índice e armazenar cada atributo de lista em outro auxiliar de lista. então você pode verificar os atributos atuais nas listas auxiliares.

boa sorte

editado: e eu não recomendaria sobrecarregar iguais. é perigoso e provavelmente contra o significado do objeto oop.

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

Com java 8, podemos fazer o seguinte para verificar se uma lista contém algum elemento de outra lista

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

Se você quiser verificar se um elemento existe em uma lista, use o método contains.

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