Podemos encontrar o hashcode
de a list
que se contém como element
?
Sei que é uma prática ruim, mas foi o que o entrevistador perguntou.
Quando executei o seguinte código, ele lança um StackOverflowError
:
public class Main {
public static void main(String args[]) {
ArrayList<ArrayList> a = new ArrayList();
a.add(a);
a.hashCode();
}
}
Agora, aqui tenho duas perguntas:
- Por que existe um
StackOverflowError
? - É possível encontrar o código hash dessa maneira?
java
java-8
stack-overflow
hashcode
Palhaço
fonte
fonte
List
interface, ohashCode
de uma lista depende de seus membros. Dado que a lista é seu próprio membro, seu código de hash depende do seuhashCode
, que depende do seuhashCode
... e assim por diante, causando recursão infinita e o queStackOverflowError
você está enfrentando. Agora, a pergunta é: por que você exige que uma lista se contenha? Posso garantir que você pode conseguir o que quer que esteja tentando fazer, de uma maneira melhor, sem a necessidade de associação recursiva como esta.Respostas:
O código de hash para
List
implementações em conformidade foi especificado na interface :Isso não exige que a implementação seja exatamente assim (consulte Como calcular o código de hash para um fluxo da mesma maneira que List.hashCode () para uma alternativa), mas o código de hash correto para uma lista que apenas se contenha um número para o qual
x == 31 + x
deve sertrue
, em outras palavras, é impossível calcular um número em conformidade.fonte
hashCode()
para retornar0
. Este tecnicamente resolvex == 31 + x
, mas ignora a exigência de que x deve ser pelo menos 1.ArrayList
implementada literalmente, levando a uma recursão, mas também não há implementação alternativa em conformidade. Observe que eu postei minha resposta em um momento em que o OP já entendia por que essa implementação específica leva a umStackOverflowError
, que foi abordada em outras respostas. Então, concentrei-me na impossibilidade geral de uma implementação em conformidade ser concluída em tempo finito com um valor.Arrays.asList("foo")
é igual aCollections.singletonList("foo")
, é igual aList.of("foo")
é igual anew ArrayList<>(List.of("foo"))
. Todas essas listas são iguais, ponto. Então, como essas listas são iguais, elas devem ter o mesmo código de hash. Não o contrário. Como eles devem ter o mesmo código de hash, ele deve estar bem definido. Independentemente de como eles o definiram (em Java 2), ele deve estar bem definido, para ser acordado por todas as implementações.List
ou tem um grande sinal de aviso "não se misture com listas comuns", compare comIdentityHashMap
e com " Esta classe não é uma implementação de mapa de uso geral! " Atenção. No primeiro caso, você já está bem em implementar,Collection
mas também adicionando os métodos de acesso baseados em índices de estilo de lista. Portanto, não existe restrição de igualdade, mas ainda funciona sem problemas com outros tipos de coleção.Confira a implementação esquelética do
hashCode
método naAbstractList
classe.Para cada elemento da lista, isso chama
hashCode
. No seu caso, a lista tem como elemento único. Agora essa ligação nunca termina. O método chama a si próprio recursivamente e a recursão continua girando até encontrar oStackOverflowError
. Então você não pode encontrarhashCode
desta maneira.fonte
Você definiu uma lista (patológica) que se contém.
De acordo com os javadocs (ou seja, a especificação), o código de hash de a
List
é definido como uma função do código de hash de cada um de seus elementos. Diz:Portanto, para calcular o hashcode de
a
, você primeiro calcula o hashcode dea
. Isso é infinitamente recursivo e leva rapidamente a um estouro de pilha.Não. Se você considerar a especificação algorítmica acima em termos matemáticos, o código hash de a
List
que se contém é uma função não computável . Não é possível computá-lo dessa maneira (usando o algoritmo acima) ou de qualquer outra maneira .fonte
Não, a documentação tem uma resposta
A documentação da estrutura da lista declara explicitamente:
Não há muito mais a dizer além disso - de acordo com a especificação Java, você não poderá calcular o hashCode para uma lista que se contenha; outras respostas entram em detalhes por que é assim, mas o ponto é que é conhecido e intencional.
fonte
A resposta de Ravindra dá uma boa explicação para o ponto 1. Para comentar a pergunta 2:
Algo é circular aqui. Pelo menos um desses 2 deve estar errado no contexto desse erro de estouro de pilha:
Agora, porque estamos lidando com um
ArrayList
, o primeiro ponto é fixo. Em outras palavras, talvez você precise de uma implementação diferente para poder calcular significativamente um código de hash de uma lista recursiva ... Pode-se estenderArrayList
e pular a adição dos códigos de hash dos elementos, algo comoUsando essa classe em vez de
ArrayList
, você poderia.Com
ArrayList
, o segundo ponto está errado. Portanto, se o entrevistador quis dizer "É possível encontrar o código hash dessa maneira (com uma lista de matrizes)?" , então a resposta é não, porque isso é um absurdo.fonte
List
contrato . Nenhuma implementação válida pode ignorar-se. A partir da especificação, você pode derivar que, se encontrar umint
número para o qualx == 31 + x
étrue
, poderá implementar um atalho válido ...List
formalmente dita a lógica do código hash a ser cumprida pelas implementações)List
fornece um cálculo de código de hash, mas que podemos substituí-lo. O único requisito real é o de códigos hash gerais: se os objetos forem iguais, os códigos hash deverão ser iguais. Esta implementação segue esse requisito.List
é uma interface. Ele define um contrato , não fornece uma implementação. Obviamente, o contrato abrange tanto a igualdade quanto o código de hash. Como o contrato geral de igualdade é que ele deve ser simétrico, se você alterar uma implementação de lista, você precisará alterar todas as implementações de lista existentes; caso contrário, você quebraria o contrato fundamental dejava.lang.Object
.Porque quando você chama a mesma função com a mesma função, ela cria uma condição de recursão que nunca termina. E para impedir esta operação, o JAVA retornará
java.lang.StackOverflowError
Abaixo está um código de exemplo que explica o cenário semelhante:
fonte