Estou tentando otimizar um trecho de código que compara elementos de lista.
Por exemplo.
public void compare(Set<Record> firstSet, Set<Record> secondSet){
for(Record firstRecord : firstSet){
for(Record secondRecord : secondSet){
// comparing logic
}
}
}
Por favor, leve em consideração que o número de registros em conjuntos será alto.
obrigado
Shekhar
java
performance
set
Shekhar
fonte
fonte
Respostas:
Realmente depende do que você deseja fazer na lógica de comparação ... ou seja, o que acontece se você encontrar um elemento em um conjunto e não no outro? Seu método tem um
void
tipo de retorno, portanto, presumo que você fará o trabalho necessário neste método.Controle mais refinado, se necessário:
Se você precisar obter os elementos que estão em um conjunto e não no outro.
EDIT:
set.removeAll(otherSet)
retorna um booleano, não um conjunto. Para usar removeAll (), você terá que copiar o conjunto e usá-lo.Se os conteúdos de
one
etwo
estiverem vazios, você saberá que os dois conjuntos são iguais. Se não, você tem os elementos que tornam os conjuntos desiguais.Você mencionou que o número de registros pode ser alto. Se a implementação subjacente for um
HashSet
, a busca de cada registro é feita aO(1)
tempo, então você não pode realmente ficar muito melhor do que isso.TreeSet
éO(log n)
.fonte
equals
é mais rápida do que duas chamadas paracontainsAll
no pior caso; veja minha resposta.Se você simplesmente deseja saber se os conjuntos são iguais, o
equals
método onAbstractSet
é implementado aproximadamente como abaixo:Observe como ele otimiza os casos comuns em que:
Depois disso,
containsAll(...)
retornaráfalse
assim que encontrar um elemento no outro conjunto que também não esteja neste conjunto. Mas se todos os elementos estiverem presentes em ambos os conjuntos, será necessário testar todos eles.O pior caso de desempenho, portanto, ocorre quando os dois conjuntos são iguais, mas não os mesmos objetos. Esse custo é normalmente
O(N)
ouO(NlogN)
dependendo da implementação dethis.containsAll(c)
.E você obtém desempenho próximo do pior caso se os conjuntos forem grandes e diferirem apenas em uma pequena porcentagem dos elementos.
ATUALIZAR
Se você deseja investir tempo em uma implementação de conjunto customizado, há uma abordagem que pode melhorar o caso "quase o mesmo".
A ideia é que você precisa pré-calcular e armazenar em cache um hash para todo o conjunto, de modo que possa obter o valor do hashcode atual do conjunto
O(1)
. Em seguida, você pode comparar o código hash para os dois conjuntos como uma aceleração.Como você poderia implementar um hashcode assim? Bem, se o hashcode definido foi:
então você poderia atualizar de forma barata o hashcode em cache do conjunto cada vez que você adicionasse ou removesse um elemento. Em ambos os casos, você simplesmente XOR o hashcode do elemento com o conjunto atual de hashcode.
Obviamente, isso pressupõe que os hashcodes do elemento são estáveis, enquanto os elementos são membros de conjuntos. Ele também assume que a função hashcode das classes de elemento oferece uma boa distribuição. Isso ocorre porque, quando os dois conjuntos de códigos de hash são iguais, você ainda precisa recorrer à
O(N)
comparação de todos os elementos.Você poderia levar essa ideia um pouco mais longe ... pelo menos em teoria.
AVISO - Isso é altamente especulativo. Um "experimento mental", se quiser.
Suponha que sua classe de elemento definido tenha um método para retornar somas de verificação de criptografia para o elemento. Agora implemente as somas de verificação do conjunto aplicando um XOR nas somas de verificação retornadas para os elementos.
O que isso nos compra?
Bem, se assumirmos que nada secreto está acontecendo, a probabilidade de que quaisquer dois elementos de conjunto desiguais tenham as mesmas somas de verificação de N bits é 2 -N . E a probabilidade de 2 conjuntos desiguais terem as mesmas somas de verificação de N bits também é 2 -N . Então, minha ideia é que você pode implementar
equals
como:De acordo com as premissas acima, isso só dará a resposta errada uma vez no tempo 2- N . Se você tornar N grande o suficiente (por exemplo, 512 bits), a probabilidade de uma resposta errada torna-se insignificante (por exemplo, aproximadamente 10 -150 ).
A desvantagem é que calcular as somas de verificação de criptografia para os elementos é muito caro, especialmente à medida que o número de bits aumenta. Portanto, você realmente precisa de um mecanismo eficaz para memorizar as somas de verificação. E isso pode ser problemático.
E a outra desvantagem é que uma probabilidade diferente de zero de erro pode ser inaceitável, não importa quão pequena seja a probabilidade. (Mas se for esse o caso ... como você lida com o caso em que um raio cósmico vira um bit crítico? Ou se ele simultaneamente vira o mesmo bit em duas instâncias de um sistema redundante?)
fonte
Existe um método no Goiaba
Sets
que pode ajudar aqui:fonte
Você tem a seguinte solução em https://www.mkyong.com/java/java-how-to-compare-two-sets/
Ou se você preferir usar uma única instrução de retorno:
fonte
equals()
método fromAbstractSet
(fornecido com o JDK), que é quase o mesmo que a solução aqui, exceto para as verificações de nulos adicionais . Java-11 Set InterfaceExiste uma solução O (N) para casos muito específicos onde:
O código a seguir assume que ambos os conjuntos são baseados em registros comparáveis. Um método semelhante pode ser baseado em um Comparador.
fonte
Se você estiver usando uma
Guava
biblioteca, é possível fazer:E então faça uma conclusão com base nisso.
fonte
Eu colocaria o secondSet em um HashMap antes da comparação. Desta forma, você reduzirá o tempo de pesquisa da segunda lista para n (1). Como isso:
fonte
fonte
Eu acho que a referência do método com o método equals pode ser usada. Assumimos que o tipo de objeto sem sombra de dúvida tem seu próprio método de comparação. Um exemplo claro e simples está aqui,
fonte
set.equals(set2)