¿Podemos encontrar el hashcode
de un list
que se contiene como element
?
Sé que esta es una mala práctica, pero esto es lo que preguntó el entrevistador.
Cuando ejecuté el siguiente código arroja un StackOverflowError
:
public class Main {
public static void main(String args[]) {
ArrayList<ArrayList> a = new ArrayList();
a.add(a);
a.hashCode();
}
}
Ahora aquí tengo dos preguntas:
- ¿Por qué hay un
StackOverflowError
? - ¿Es posible encontrar el código hash de esta manera?
java
java-8
stack-overflow
hashcode
bufón
fuente
fuente
List
interfaz, lahashCode
lista depende de sus miembros. Dado que la lista es su propio miembro, su código hash depende de suhashCode
, que depende de suhashCode
... y así sucesivamente, causando una recursión infinita y elStackOverflowError
que se está encontrando. Ahora la pregunta es: ¿por qué necesita una lista para contenerse? Puedo garantizarle que puede lograr lo que sea que esté tratando de hacer, de una mejor manera, sin requerir una membresía recursiva como esta.Respuestas:
El código hash para
List
implementaciones conformes se ha especificado en la interfaz :Esto no requiere que la implementación se vea exactamente así (consulte Cómo calcular el código hash para una secuencia de la misma manera que List.hashCode () para una alternativa), pero el código hash correcto para una lista que solo se contiene ser un número para el cual
x == 31 + x
debe sertrue
, en otras palabras, es imposible calcular un número conforme.fuente
hashCode()
para devolver0
. Esto resuelve técnicamentex == 31 + x
pero ignora el requisito de que x debe ser al menos 1.ArrayList
implementa literalmente, lo que lleva a una recursión, pero tampoco hay una implementación alternativa conforme. Tenga en cuenta que publiqué mi respuesta en un momento en que el OP ya entendía por qué esta implementación en particular conduce a unaStackOverflowError
, que se ha abordado en otras respuestas. Así que me concentré en la imposibilidad general de que una implementación conforme se complete en un tiempo finito con un valor.Arrays.asList("foo")
es igual aCollections.singletonList("foo")
, es igual aList.of("foo")
es igual anew ArrayList<>(List.of("foo"))
. Todas estas listas son iguales, punto. Entonces, dado que estas listas son iguales, deben tener el mismo código hash. No al revés. Como deben tener el mismo código hash, debe estar bien definido. Independientemente de cómo lo definieron (en Java 2), debe estar bien definido, para ser acordado por todas las implementaciones.List
o tiene una gran señal de advertencia "no mezclar con listas comunes", se compara conIdentityHashMap
su "¡ Esta clase no es una implementación de Mapa de propósito general! " advertencia. En el primer caso, ya está bien con la implementación,Collection
pero también agrega los métodos de acceso basados en el índice de estilo de lista. Entonces, no existe restricción de igualdad, pero aún funciona sin problemas con otros tipos de colección.Echa un vistazo a la implementación esquelética del
hashCode
método enAbstractList
clase.Para cada elemento de la lista, esto llama
hashCode
. En su caso, la lista se tiene como único elemento. Ahora esta llamada nunca termina. El método se llama a sí mismo de forma recursiva y la recursión se sigue enrollando hasta que encuentra elStackOverflowError
. Entonces no puedes encontrar elhashCode
camino de esta manera.fuente
Ha definido una lista (patológica) que se contiene a sí misma.
Según los javadocs (es decir, la especificación), el código hash de a
List
se define como una función del código hash de cada uno de sus elementos. Dice:Entonces, para calcular el código hash de
a
, primero debes calcular el código hash dea
. Eso es infinitamente recursivo y conduce rápidamente a un desbordamiento de pila.No. Si considera la especificación algorítmica anterior en términos matemáticos, el código hash de un
List
que se contiene es una función no computable . No es posible calcularlo de esta manera (usando el algoritmo anterior) ni de ninguna otra manera .fuente
No, la documentación tiene una respuesta.
La documentación de la estructura de la Lista establece explícitamente:
No hay mucho más que decir más allá de eso: según la especificación de Java, no podrá calcular hashCode para una lista que se contiene a sí misma; otras respuestas entran en detalles por qué es así, pero el punto es que es conocido e intencional.
fuente
La respuesta de Ravindra da una buena explicación para el punto 1. Para comentar sobre la pregunta 2:
Algo es circular aquí. Al menos uno de estos 2 debe estar equivocado en el contexto de este error de desbordamiento de pila:
Ahora, debido a que estamos tratando con un
ArrayList
, el primer punto es fijo. En otras palabras, tal vez necesite una implementación diferente para poder calcular significativamente un código hash de una lista recursiva ... Se podría extenderArrayList
y omitir la adición de códigos hash de elementos, algo así comoUsando tal clase en lugar de
ArrayList
, podrías.Con
ArrayList
, el segundo punto está mal. Entonces, si el entrevistador quería decir "¿Es posible encontrar código hash de esta manera (con una lista de matriz)?" , entonces la respuesta es no, porque eso es absurdo.fuente
List
contrato . Ninguna implementación válida puede saltarse a sí misma. A partir de la especificación, puede deducir que si encuentra unint
número para el cualx == 31 + x
estrue
, entonces puede implementar un atajo válido ...List
dicta formalmente la lógica del código hash que deben cumplir las implementaciones)List
proporciona un cálculo de código hash, pero que podríamos anularlo. El único requisito real es el de los códigos hash generales: si los objetos son iguales, entonces los códigos hash deben ser iguales. Esta implementación sigue ese requisito.List
es una interfaz. Define un contrato , no proporciona una implementación. Por supuesto, el contrato cubre tanto la igualdad como el código hash. Dado que el contrato general de igualdad es que tiene que ser simétrico, si cambia la implementación de una lista, tendría que cambiar todas las implementaciones de listas existentes, de lo contrario, incluso rompería el contrato fundamentaljava.lang.Object
.Porque cuando llamas a la misma función con la misma función, creará una condición de recursión que nunca termina. Y para evitar esta operación JAVA regresará
java.lang.StackOverflowError
A continuación se muestra un código de ejemplo que explica un escenario similar:
fuente