Alguém pode me explicar em termos simples, por que esse código gera uma exceção "O método de comparação viola seu contrato geral!" E como faço para corrigi-lo?
private int compareParents(Foo s1, Foo s2) {
if (s1.getParent() == s2) return -1;
if (s2.getParent() == s1) return 1;
return 0;
}
java
comparator
n00bster
fonte
fonte
s1.getParent().equals(s2)
vez des1.getParent() == s2
.s1
é o pai des2
, es2
não é o pai des1
. EntãocompareParents(s1, s2)
é0
, mascompareParents(s2, s1)
é1
. Isso não faz sentido. (Além disso, não é transitiva, como aix mencionados abaixo.)Respostas:
Seu comparador não é transitivo.
Let
A
ser o pai deB
, eB
ser o pai deC
. DesdeA > B
eB > C
, então deve ser o casoA > C
. No entanto, se o seu comparador for chamadoA
eC
retornará zero, o que significaA == C
. Isso viola o contrato e, portanto, gera a exceção.É bastante agradável da biblioteca detectar isso e informar você, em vez de se comportar de maneira irregular.
Uma maneira de satisfazer o requisito de transitividade
compareParents()
é atravessar agetParent()
cadeia em vez de apenas olhar para o ancestral imediato.fonte
java.util.Arrays.sort
stackoverflow.com/questions/7849539/…Só porque é isso que recebi quando pesquisei esse erro no Google, meu problema era que eu tinha
o
value >= other.value
(obviamente) deve ser realmente,value > other.value
para que você possa retornar 0 com objetos iguais.fonte
value
é um NaN (sevalue
é umdouble
oufloat
), também falharia.A violação do contrato geralmente significa que o comparador não está fornecendo o valor correto ou consistente ao comparar objetos. Por exemplo, convém executar uma comparação de cadeias e forçar cadeias vazias para classificar até o final com:
Mas isso ignora o caso em que AMBOS um e dois estão vazios - e, nesse caso, o valor errado é retornado (1 em vez de 0 para mostrar uma correspondência), e o comparador relata isso como uma violação. Deveria ter sido escrito como:
fonte
Mesmo que seu compareTo mantenha transitividade na teoria, os erros sutis às vezes atrapalham as coisas ... como erro aritmético de ponto flutuante. Isso aconteceu comigo. este foi o meu código:
A propriedade transitiva claramente é válida, mas por algum motivo eu estava recebendo a IllegalArgumentException. E acontece que, devido a pequenos erros na aritmética de ponto flutuante, os erros de arredondamento causavam a quebra da propriedade transitiva onde não deveriam! Então, eu reescrevi o código para considerar realmente pequenas diferenças 0, e funcionou:
fonte
No nosso caso, estávamos recebendo esse erro porque acidentalmente invertemos a ordem de comparação de s1 e s2. Então, cuidado com isso. Era obviamente muito mais complicado que o seguinte, mas esta é uma ilustração:
fonte
O Java não verifica a consistência em sentido estrito, apenas o notifica se encontrar problemas sérios. Também não fornece muitas informações sobre o erro.
Fiquei intrigado com o que está acontecendo no meu classificador e fiz um rigoroso consistencyChecker, talvez isso ajude você:
fonte
Compare
,Convert
(e potencialmente outros) não são definidos. Atualize o snippet de código com um exemplo independente.checkConsi(s)tency
e remover todas as@param
declarações redundantes para tornar o código mais legível.No meu caso, eu estava fazendo algo como o seguinte:
O que eu esqueci de verificar foi quando a.someField e b.someField são nulos.
fonte
Eu já vi isso acontecer em um pedaço de código em que a verificação frequentemente recorrente de valores nulos foi realizada:
fonte
Se
compareParents(s1, s2) == -1
entãocompareParents(s2, s1) == 1
é esperado. Com o seu código nem sempre é verdade.Especificamente se
s1.getParent() == s2 && s2.getParent() == s1
. É apenas um dos possíveis problemas.fonte
A edição da configuração da VM funcionou para mim.
fonte
-
o início da solução proposta. Talvez você tenha planejado algo como uma lista de marcadores de um item.Você não pode comparar dados de objetos como este:
s1.getParent() == s2
- isto irá comparar as referências de objetos. Você deve substituirequals function
a classe Foo e compará-las dessa maneiras1.getParent().equals(s2)
fonte