Eu tenho dois ArrayList
s do tipoAnswer
(classe criada por você).
Gostaria de comparar as duas listas para ver se elas contêm o mesmo conteúdo, mas sem que a ordem seja importante.
Exemplo:
//These should be equal.
ArrayList<String> listA = {"a", "b", "c"}
ArrayList<String> listB = {"b", "c", "a"}
List.equals
afirma que duas listas são iguais se contiverem o mesmo tamanho, conteúdo e ordem dos elementos. Eu quero a mesma coisa, mas sem ordem.
Existe uma maneira simples de fazer isso? Ou precisarei fazer um loop for aninhado e verificar manualmente cada índice das duas listas?
Nota: Não posso alterá-los ArrayList
para outro tipo de lista, eles precisam permanecer assim.
Respostas:
Você pode classificar as duas listas usando
Collections.sort()
e depois usar o método equals. Uma solução um pouco melhor é verificar primeiro se eles têm o mesmo comprimento antes de fazer o pedido, se não são, então não são iguais, depois classificam e usam iguais. Por exemplo, se você tivesse duas listas de Strings, seria algo como:fonte
Collections.sort
faz) - ou seja, passe uma cópia.one = new ArrayList<String>(one); two = new ArrayList<String>(two);
para evitar arruinar os argumentos.if(one == null || two == null || one.size() != two.size()){ return false; }
porque você já está verificando se um e dois são nulos.Provavelmente, a maneira mais fácil para qualquer lista seria:
fonte
[a, b, c]
e[c, b, a, b]
é considerado como tendo o mesmo conteúdo. Essa resposta diria que sim, mas pode ser que, para o OP, eles não (já que um contém uma duplicata e o outro não). Para não falar das questões de eficiência.Coleções Apache Commons para o resgate mais uma vez:
Documentos:
fonte
false
? Veja minha resposta.implementation 'org.apache.commons:commons-collections4:4.3'
me deixou com umCaused by: com.android.builder.dexing.DexArchiveBuilderException: Failed to process
caminho de erro .fonte
count
torna negativo, a simplificação do corpo do laço paraif(!counts.containsKey(item) || --counts.get(item).count < 0) return false;
Também, o terceiro circuito poderia ser simplificada parafor(Count c: counts.values()) if(c.count != 0) return false;
counts
está vazio"), mas não queria obscurecer o ponto principal: o uso de um mapa basicamente o transforma em um problema de O (N + M) e é o maior aumento que você provavelmente obterá.Map.merge
números inteiros em caixa pode ser mais simples e mais eficiente para a maioria dos casos de uso. Veja também esta resposta ...Eu diria que essas respostas perdem um truque.
Bloch, em seu Java efetivo essencial, maravilhoso e conciso , diz, no item 47, o título "Conheça e use as bibliotecas", "Para resumir, não reinvente a roda". E ele dá várias razões muito claras por que não.
Existem algumas respostas aqui que sugerem métodos da
CollectionUtils
biblioteca Apache Commons Collections, mas nenhuma encontrou a maneira mais bonita e elegante de responder a essa pergunta :Culpados : ou seja, os elementos que não são comuns a ambos
Lists
. Determinar a quais culpados pertencemlist1
e a quallist2
é relativamente simples usarCollectionUtils.intersection( list1, culprits )
eCollectionUtils.intersection( list2, culprits )
.No entanto, ele tende a desmoronar em casos como {"a", "a", "b"}
disjunction
com {"a", "b", "b"} ... exceto que isso não é uma falha do software, mas inerente à natureza das sutilezas / ambiguidades da tarefa desejada.Você sempre pode examinar o código fonte (l. 287) para uma tarefa como essa, produzida pelos engenheiros da Apache. Um benefício do uso de seu código é que ele foi testado e testado exaustivamente, com muitos casos extremos e dicas antecipadas e resolvidas. Você pode copiar e ajustar esse código para o conteúdo do seu coração, se necessário.
NB: No começo, fiquei desapontado por nenhum dos
CollectionUtils
métodos fornecer uma versão sobrecarregada, permitindo que você imponha a sua própriaComparator
(para que você possa redefinirequals
para atender às suas finalidades).Mas a partir das coleções4 4.0, há uma nova classe,
Equator
que "determina a igualdade entre objetos do tipo T". Ao examinar o código-fonte de collections4 CollectionUtils.java, eles parecem estar usando isso com alguns métodos, mas, pelo que sei, isso não se aplica aos métodos na parte superior do arquivo, usando aCardinalityHelper
classe ... incluadisjunction
eintersection
.Suponho que o pessoal do Apache ainda não tenha resolvido isso porque não é trivial: você teria que criar algo como uma classe "AbstractEquatingCollection", que, em vez de usar os métodos
equals
ehashCode
métodos inerentes aos seus elementos, teria que usá-los deEquator
todos os métodos básicos, comoadd
,contains
etc. Nota: quando você olha para o código-fonte,AbstractCollection
não implementaadd
nem suas subclasses abstratas comoAbstractSet
... você precisa esperar até as classes concretas comoHashSet
eArrayList
antesadd
é implementado. Bastante dor de cabeça.Enquanto isso, assista a este espaço, suponho. A solução provisória óbvia seria agrupar todos os seus elementos em uma classe de wrapper personalizada que usa
equals
ehashCode
implementar o tipo de igualdade que você deseja ... e então manipularCollections
esses objetos de wrapper.fonte
Se a cardinalidade dos itens não importa (o que significa: elementos repetidos são considerados um), existe uma maneira de fazer isso sem precisar classificar:
boolean result = new HashSet<>(listA).equals(new HashSet<>(listB));
Isso criará um
Set
fora de cada umList
e, em seguida, usaráHashSet
oequals
método que (é claro) desconsidera a ordem.Se a cardinalidade é importante, você deve limitar-se às instalações fornecidas por
List
; A resposta de @schoen seria mais adequada nesse caso.fonte
Converter as listas no Multiset do Guava funciona muito bem. Eles são comparados independentemente da ordem e os elementos duplicados também são levados em consideração.
fonte
Isso é baseado na solução @cHao. Incluí várias correções e melhorias de desempenho. Isso funciona aproximadamente duas vezes mais rápido que a solução de cópia ordenada igual. Funciona para qualquer tipo de coleção. Coleções vazias e nulas são consideradas iguais. Use a sua vantagem;)
fonte
false
quando uma chave não existe ou sua contagem se torna negativa. Como o tamanho total de ambas as listas corresponde (isso foi verificado antecipadamente), é impossível ter valores diferentes de zero após o segundo loop, pois não pode haver valores positivos para uma chave sem valores negativos para outra chave.Pense em como você faria isso, sem um computador ou linguagem de programação. Dou a você duas listas de elementos e você precisa me dizer se eles contêm os mesmos elementos. Como você faria?
Uma abordagem, como mencionado acima, é classificar as listas e, em seguida, ir elemento por elemento para ver se são iguais (que é o que
List.equals
faz). Isso significa que você pode modificar as listas ou copiá-las - e sem conhecer a tarefa, não sei se é permitido um ou ambos.Outra abordagem seria percorrer cada lista, contando quantas vezes cada elemento aparece. Se ambas as listas tiverem as mesmas contagens no final, elas terão os mesmos elementos. O código para isso seria converter cada lista em um mapa
elem -> (# of times the elem appears in the list)
e, em seguida, chamarequals
os dois mapas. Se os mapas foremHashMap
, cada uma dessas traduções é uma operação O (N), assim como a comparação. Isso fornecerá um algoritmo bastante eficiente em termos de tempo, com o custo de memória extra.fonte
Eu tive esse mesmo problema e surgiu com uma solução diferente. Este também funciona quando duplicatas estão envolvidas:
Vantagens em comparação com outras soluções:
[1,2,3,3]
e outra matriz, a[1,2,2,3]
maioria das soluções aqui informa que elas são iguais quando não se considera o pedido. Esta solução evita isso removendo elementos iguais das listas temporárias;equals
) e não faz referência a igualdade (==
);implement Comparable
) para que esta solução funcione.fonte
Se você não deseja classificar as coleções e precisa do resultado de que ["A" "B" "C"] não seja igual a ["B" "B" "A" "C"],
não é suficiente, você provavelmente também deve verificar o tamanho:
fonte
Solução que aproveita o método de subtração CollectionUtils:
fonte
Se você se preocupa com a ordem, basta usar o método equals:
Se você não se importa com o pedido, use este
fonte
Melhor dos dois mundos [@DiddiZ, @Chalkos]: este baseia-se principalmente no método @Chalkos, mas corrige um erro (ifst.next ()) e melhora as verificações iniciais (retiradas do @DiddiZ), além de remover a necessidade de copie a primeira coleção (apenas remove os itens de uma cópia da segunda coleção).
Não exigindo uma função ou classificação de hash e possibilitando a existência precoce da desigualdade, essa é a implementação mais eficiente ainda. Isto é, a menos que você tenha um comprimento de coleção na casa dos milhares ou mais e uma função de hash muito simples.
fonte
É uma maneira alternativa de verificar a igualdade das listas de matrizes que podem conter valores nulos:
fonte
Minha solução para isso. Não é tão legal, mas funciona bem.
fonte
Nesse caso, as listas {"a", "b"} e {"b", "a"} são iguais. E {"a", "b"} e {"b", "a", "c"} não são iguais. Se você usar uma lista de objetos complexos, lembre-se de substituir o método equals , pois containsAll o usa dentro.
fonte
AbstractCollection.containsAll()
. Você tem que permitir ter elementos duplicados, como estamos falandoLists
, não sobreSets
. Por favor, veja minha resposta.