55 votes

Faut-il autoriser l'ajout d'un HashSet à lui-même en Java?

Selon le contrat pour un Jeu en Java, "il n'est pas permis à un ensemble de contenir lui-même comme un élément" (source). Toutefois, cela est possible dans le cas d'un HashSet d'Objets, comme montré ici:

Set<Object> mySet = new HashSet<>();
mySet.add(mySet);
assertThat(mySet.size(), equalTo(1));

Cette affirmation passe, mais je m'attends à voir le comportement de disposer de l'ensemble résultant sera de 0, ou de jeter une Exception. Je me rends compte de la sous-tendent la mise en œuvre d'un HashSet est une table de hachage, mais il semble qu'il devrait y avoir une égalité de vérifier avant l'ajout d'un élément à éviter la violation de ce contrat, non?

54voto

Marco13 Points 14743

D'autres l'ont déjà souligné pourquoi il est discutable d'un point de vue mathématique, en se référant au paradoxe de Russell.

Ce n'est pas la réponse à votre question sur une technique de niveau, cependant.

Donc, nous allons disséquer ce:

Tout d'abord, une fois de plus, la partie pertinente de la JavaDoc de l' Set interface:

Remarque: le plus Grand soin doit être exercé si mutable objets sont utilisés comme éléments. Le comportement d'un ensemble n'est pas spécifié si la valeur d'un objet est modifié d'une manière qui affecte égale comparaisons tandis que l'objet est un élément de l'ensemble. Un cas particulier de cette interdiction, c'est qu'il n'est pas permis à un ensemble de contenir lui-même comme un élément.

Fait intéressant, la JavaDoc de l' List interface fait un semblable, bien que un peu plus faible, et dans le même temps, plus technique, de la déclaration:

Alors qu'il est permis pour que les listes contiennent eux-mêmes comme des éléments, une extrême prudence est recommandée: l' equals et hashCode méthodes ne sont plus bien défini sur une telle liste.

Et enfin, le noeud est dans la JavaDoc de l' Collection interface, qui est l'ancêtre commun à la fois l' Set et de la List interface:

Certaines opérations de collecte qui effectuent récursive de la traversée de la collection peut échouer avec une exception pour l'auto-référentielle cas où la collection, directement ou indirectement, contient en lui-même. Cela comprend l' clone(), equals(), hashCode() et toString() méthodes. Les implémentations peuvent éventuellement gérer l'auto-référentielle de scénario, cependant, la plupart des implémentations actuelles ne le font pas.

(Accent mis par moi)

La partie en gras est une allusion à pourquoi l'approche que vous avez proposé dans votre question ne serait pas suffisant:

il semble qu'il devrait y avoir une égalité de vérifier avant l'ajout d'un élément à éviter la violation de ce contrat, non?

Ce ne serait pas vous aider ici. Le point clé est que vous aurez toujours des problèmes lorsqu'la collection , directement ou indirectement, contenir lui-même. Imaginez ce scénario:

Set<Object> setA = new HashSet<Object>();
Set<Object> setB = new HashSet<Object>();
setA.add(setB);
setB.add(setA);

De toute évidence, aucun de ces ensembles contient lui-même directement. Mais chacun d'entre eux contient l'autre - et, par conséquent, lui-même indirectement. Ce ne pouvait être évitée par un simple référentiel de contrôle d'égalité (à l'aide d' == dans la add méthode).


Éviter un tel "état incohérent" est pratiquement impossible dans la pratique. Bien sûr, il est possible, en théorie, à l'aide de référentiel d'Accessibilité des calculs. En fait, le Garbage Collector a essentiellement pour faire exactement cela!

Mais il devient impossible , dans la pratique, lorsque les classes personnalisées sont impliqués. Imaginez une classe comme ceci:

class Container {

    Set<Object> set;

    @Override 
    int hashCode() {
        return set.hashCode(); 
    }
}

Et de déconner avec le ce et de ses set:

Set<Object> set = new HashSet<Object>();
Container container = new Container();
container.set = set;
set.add(container);

L' add méthode de Set a pratiquement pas de moyen de détecter si l'objet est ajouté il y a quelques (indirecte) de référence à l'ensemble lui-même.

Longue histoire courte:

Vous ne pouvez pas empêcher le programmeur de gâcher les choses.

23voto

Makoto Points 23751

L'ajout de la collection en elle-même une fois que les causes de l'épreuve à passer. Ajouter deux fois les causes de l' StackOverflowError qui vous étaient à la recherche d'.

À partir d'un point de vue développeur, il n'a pas de sens pour exécuter une vérification dans le code sous-jacent pour l'en empêcher. Le fait que vous obtenez un StackOverflowError dans votre code si vous tentez de le faire trop grand nombre de fois, ou de calculer le hashCode - ce qui entraînerait un instant de trop - plein doit être suffisant pour s'assurer que pas sain d'esprit développeur de maintenir ce type de code dans leur code de base.

13voto

Polygnome Points 4766

Vous avez besoin de lire la doc et le citer intégralement:

Le comportement d'un ensemble n'est pas spécifié si la valeur d'un objet est modifié d'une manière qui affecte égale comparaisons tandis que l'objet est un élément de l'ensemble. Un cas particulier de cette interdiction, c'est qu'il n'est pas permis à un ensemble de contenir lui-même comme un élément.

La restriction effective est dans la première phrase. Le comportement est indéterminé si un élément d'un ensemble est muté.

Depuis l'ajout d'un ensemble de lui-même se transforme, et l'ajouter à nouveau se transforme à nouveau, le résultat est indéfini.

Notez que la restriction est que le comportement est indéterminé, et qu'un cas particulier de cette restriction est d'ajouter la série à lui-même.

Donc le doc a dit, en d'autres termes, que l'ajout d'un ensemble de conduit elle-même à un comportement non spécifié, qui est ce que vous voyez. C'est à la mise en œuvre concrète de traiter avec (ou pas).

9voto

EJoshuaS Points 7022

Je suis d'accord avec vous que, d'un point de vue mathématique, ce comportement n'a vraiment pas de sens.

Il y a deux questions intéressantes ici: d'abord, dans quelle mesure les concepteurs de l' Set interface d'essayer de mettre en œuvre un ensemble mathématique? Deuxièmement, même si elles n'étaient pas, dans quelle mesure ce exempter des règles de la théorie des ensembles?

Pour la première question, je vais vous à la documentation de l'Ensemble:

Une collection qui ne contient pas les éléments en double. Plus formellement, les ensembles contiennent pas de paire d'éléments e1 et e2 tels que e1.equals(e2), et au plus un élément de valeur null. Comme le laisse entendre son nom, cette interface des modèles mathématiques mis l'abstraction.

Il est intéressant de mentionner ici que les formulations actuelles de la théorie des ensembles n'est pas permis de séries de membres d'eux-mêmes. (Voir l' Axiome de régularité). Cela est dû en partie au Paradoxe de Russell, qui a exposé une contradiction dans les naïve de la théorie des ensembles (qui permet de définir à toute collection d'objets - il n'y a pas d'interdiction à l'encontre de jeux, y compris eux-mêmes). C'est souvent illustré par le salon de coiffure Paradoxe: supposons que, dans une ville, un barbier rase tous les hommes - et seulement les hommes - qui ne rasez pas eux-mêmes. Question: le salon de coiffure se raser lui-même? S'il le fait, il viole la deuxième contrainte; s'il ne le fait pas, il viole la première contrainte. C'est clairement logiquement impossible, mais il est parfaitement admissible en vertu des règles de naïve de la théorie des ensembles (c'est pourquoi la nouvelle "norme" de la formulation de la théorie des ensembles interdit expressément jeux de contenant eux-mêmes).

Il n'y a plus de discussion de cette question sur les Mathématiques.SE au sujet de pourquoi les jeux ne peuvent pas être un élément d'eux-mêmes.

Cela dit, cela amène à la deuxième question: même si les concepteurs n'avaient pas été explicitement essayer de modéliser un ensemble mathématique, serait-ce complètement "exonéré" de la les problèmes associés avec naïve de la théorie des ensembles? Je ne crois pas - je pense que la plupart des problèmes qui, en proie naïve de la théorie des ensembles hantera toute sorte de collection qui n'était pas suffisamment limités par des moyens qui étaient analogues aux naïve de la théorie des ensembles. En effet, j'ai peut-être lu trop en ce, mais la première partie de la définition de l' Set dans la documentation ressemble étrangement le concept intuitif d'un ensemble naïve de la théorie des ensembles:

Une collection qui ne contient pas les éléments en double.

Certes (et de leur crédit), ils ne placez au moins certaines contraintes sur cela plus tard (y compris en indiquant que vous ne devriez pas essayer d'avoir un Ensemble contient lui-même), mais vous pourriez question de savoir si c'est vraiment "assez" pour éviter les problèmes avec naïve de la théorie des ensembles. C'est pourquoi, par exemple, vous avez un "tortues tout le chemin vers le bas" problème lorsque vous essayez de calculer le code de hachage d'un HashSet qui contient elle-même. Ce n'est pas, comme certains l'ont suggéré, simplement un problème d'ordre pratique - c'est une illustration de l'théoriques fondamentales des problèmes avec ce type de formulation.

Comme une brève digression, je dois reconnaître qu'il y a, bien sûr, certaines restrictions sur la façon dont étroitement toute collecte, de toute classe peut vraiment modèle d'un ensemble mathématique. Par exemple, Java de la documentation met en garde contre les dangers de l', y compris mutable objets dans un ensemble. Certains autres langages tels que Python, au moins tenter de l'interdiction de nombreux types d'objets mutables entièrement:

L'ensemble des classes sont mises en œuvre à l'aide de dictionnaires. En conséquence, les exigences pour définir les éléments sont les mêmes que ceux pour les clés de dictionnaire, à savoir que l'élément définit à la fois __eq__() et __hash__(). En conséquence, les ensembles ne peut pas contenir mutable éléments comme les listes ou les dictionnaires. Cependant, elles peuvent contenir immuable collections tels que les n-uplets ou des instances de ImmutableSet. Pour plus de commodité dans la mise en œuvre des ensembles d'ensembles, l'intérieur des ensembles sont automatiquement converties en forme immuable, par exemple, Set([Set(['dog'])]) est transformé en Set([ImmutableSet(['dog'])]).

Deux autres différences majeures que d'autres l'ont souligné, sont

  • Java jeux sont mutable
  • Java jeux sont finis. Évidemment, ce sera vrai de toute classe de collection: en dehors de préoccupations au sujet de l'infini réel, les ordinateurs ont seulement une quantité limitée de mémoire. (Certaines langues, comme Haskell, ont paresseux infini des structures de données; cependant, à mon avis, un lawlike choix de la séquence semble être un moyen plus naturel modèle ces que le classique de la théorie des ensembles, mais c'est juste mon avis).

TL;DR No, il ne devrait pas être permis (ou, au moins, vous ne devriez jamais le faire) parce que les ensembles peuvent pas être membres d'eux-mêmes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X