Pouvons-nous trouver le hashcode
d'un list
qui se contient comme element
?
Je sais que c'est une mauvaise pratique, mais c'est ce que l'intervieweur a demandé.
Lorsque j'ai exécuté le code suivant, il lance un StackOverflowError
:
public class Main {
public static void main(String args[]) {
ArrayList<ArrayList> a = new ArrayList();
a.add(a);
a.hashCode();
}
}
Maintenant, ici, j'ai deux questions:
- Pourquoi y en a-t-il
StackOverflowError
? - Est-il possible de trouver le code de hachage de cette manière?
List
interface, le fonctionnementhashCode
d'une liste dépend de ses membres. Étant donné que la liste est son propre membre, son code de hachage dépend de sonhashCode
, qui dépend de sonhashCode
... et ainsi de suite, provoquant une récursion infinie et le queStackOverflowError
vous rencontrez. Maintenant, la question est: pourquoi avez-vous besoin d'une liste pour se contenir? Je peux vous garantir que vous pouvez réaliser tout ce que vous essayez de faire, d'une meilleure manière, sans exiger une adhésion récursive comme celle-ci.Réponses:
Le code de hachage pour les
List
implémentations conformes a été spécifié dans l'interface :Cela ne nécessite pas que l'implémentation ressemble exactement à cela (voir Comment calculer le code de hachage pour un flux de la même manière que List.hashCode () pour une alternative), mais le code de hachage correct pour une liste ne contenant que lui-même serait être un nombre pour lequel
x == 31 + x
doit êtretrue
, en d'autres termes, il est impossible de calculer un nombre conforme.la source
hashCode()
à retourner0
. Cela résout techniquementx == 31 + x
mais ignore l'exigence selon laquelle x doit être au moins 1.ArrayList
implémente littéralement, conduisant à une récursivité, mais il n'y a pas non plus d'implémentation conforme conforme. Notez que j'ai posté ma réponse à un moment où le PO a déjà compris pourquoi cette mise en œuvre particulière conduit à unStackOverflowError
, qui a été traité dans d'autres réponses. Je me suis donc concentré sur l'impossibilité générale d'une implémentation conforme se terminant en temps fini avec une valeur.Arrays.asList("foo")
est égal àCollections.singletonList("foo")
, est égal àList.of("foo")
est égal ànew ArrayList<>(List.of("foo"))
. Toutes ces listes sont égales, point. Puis, comme ces listes sont égales, elles doivent avoir le même code de hachage. Pas l'inverse. Puisqu'ils doivent avoir le même code de hachage, il doit être bien défini. Quelle que soit la façon dont ils l'ont définie (de retour en Java 2), elle doit être bien définie, pour être acceptée par toutes les implémentations.List
soit présente un grand signe d'avertissement «ne pas mélanger avec des listes ordinaires», comparer avecIdentityHashMap
et sa « Cette classe n'est pas une implémentation Map à usage général! " Attention. Dans le premier cas, vous êtes déjà prêt à implémenterCollection
mais aussi à ajouter les méthodes d'accès basées sur l'index de style de liste. Ensuite, aucune contrainte d'égalité n'existe, mais elle fonctionne toujours sans problème avec d'autres types de collection.Découvrez l'implémentation squelettique de la
hashCode
méthode enAbstractList
classe.Pour chaque élément de la liste, cela appelle
hashCode
. Dans votre cas, la liste a elle-même comme seul élément. Maintenant, cet appel ne se termine jamais. La méthode s'appelle récursivement et la récursivité continue de s'enrouler jusqu'à ce qu'elle rencontre leStackOverflowError
. Donc, vous ne pouvez pas trouver dehashCode
cette façon.la source
Vous avez défini une liste (pathologique) qui se contient.
Selon les javadocs (c'est-à-dire la spécification), le hashcode d'un
List
est défini en fonction du hashcode de chacun de ses éléments. Ça dit:Donc, pour calculer le code de hachage de
a
, vous devez d'abord calculer le code de hachage dea
. C'est infiniment récursif et conduit rapidement à un débordement de pile.Non. Si vous considérez la spécification algorithmique ci-dessus en termes mathématiques, le code de hachage d'un
List
qui se contient est une fonction non calculable . Il n'est pas possible de le calculer de cette façon (en utilisant l'algorithme ci-dessus) ou de toute autre manière .la source
Non, la documentation a une réponse
La documentation de la structure List indique explicitement:
Il n'y a pas grand-chose à dire au-delà de cela - selon la spécification Java, vous ne pourrez pas calculer hashCode pour une liste qui se contient; d'autres réponses expliquent en détail pourquoi il en est ainsi, mais le fait est qu'elle est connue et intentionnelle.
la source
La réponse de Ravindra donne une bonne explication pour le point 1. Pour commenter la question 2:
Quelque chose est circulaire ici. Au moins l'un de ces 2 doit être erroné dans le contexte de cette erreur de débordement de pile:
Maintenant, parce que nous avons affaire à un
ArrayList
, le premier point est fixe. En d'autres termes, vous avez peut-être besoin d'une implémentation différente pour pouvoir calculer de manière significative le code de hachage d'une liste récursive ... On pourrait étendreArrayList
et ignorer l'ajout des codes de hachage des éléments, quelque chose commeArrayList
Vous pourriez utiliser une telle classe au lieu de .Avec
ArrayList
, le deuxième point est faux. Donc, si l'intervieweur voulait dire "Est-il possible de trouver du code de hachage de cette manière (avec une liste de tableaux)?" , alors la réponse est non, car c'est absurde.la source
List
contrat . Aucune implémentation valide ne peut se sauter. De la spécification, vous pouvez déduire que si vous trouvez unint
numéro pour lequelx == 31 + x
esttrue
, vous pouvez mettre en œuvre un raccourci valide ...List
dicte formellement la logique du code de hachage à respecter par les implémentations)List
fournit un calcul de code de hachage, mais que nous pourrions le remplacer. La seule vraie exigence est celle des codes de hachage généraux: si les objets sont égaux, alors les codes de hachage doivent être égaux. Cette implémentation suit cette exigence.List
est une interface. Il définit un contrat , il ne fournit pas de mise en œuvre. Bien sûr, le contrat couvre à la fois l'égalité et le code de hachage. Puisque le contrat général d'égalité est qu'il doit être symétrique, si vous changez une implémentation de liste, vous devrez changer toutes les implémentations de liste existantes, sinon, vous briseriez même le contrat fondamental dejava.lang.Object
.Parce que lorsque vous appelez la même fonction avec la même fonction, cela crée une condition de récursivité qui ne se termine jamais. Et pour empêcher cette opération, JAVA reviendra
java.lang.StackOverflowError
Voici un exemple de code qui explique un scénario similaire:
la source