champs mutables pour les objets dans un jeu Java
-
21-08-2019 - |
Question
Ai-je raison de supposer que si vous avez un objet qui est contenu dans un Java Set <> (ou une clé dans une carte <> pour cette matière), tous les champs qui sont utilisés pour déterminer l'identité ou la relation (via hashCode()
, equals()
, etc. compareTo()
) ne peut être modifiée sans causer un comportement non spécifié pour les opérations sur la collecte? (Edit: comme allusion dans cette autre question )
(En d'autres termes, ces champs doivent être soit immuable, ou vous devez exiger de l'objet à supprimer de la collection, puis a changé, puis réinséré.)
La raison pour laquelle je demande est que je lisais le Guide de référence Hibernate Annotations __gVirt_NP_NN_NNPS<__ et il a un exemple où il y a un mais la classe HashSet<Toy>
a des champs Toy
et name
qui sont mutable serial
et sont également utilisés dans le calcul .. <=> . un drapeau rouge a explosé dans ma tête et je voulais juste vous assurer que je compris les implications.
La solution
Le Javadoc dit Set
Remarque: Un grand soin doit être exercé si objets mutables sont utilisés comme ensemble éléments. Le comportement d'un ensemble n'est pas spécifié si la valeur d'un objet est changé d'une manière qui affecte équivaut à des comparaisons alors que l'objet est un élément de l'ensemble. Un cas particulier de cette interdiction est que ce n'est pas admissible pour un ensemble de contenir lui-même comme un élément.
Cela signifie simplement que vous pouvez utiliser des objets mutables dans un ensemble, et même les changer. Vous devez vous assurer que le changement n'a pas d'impact de la façon dont le trouve les éléments HashSet
. Pour hashCode()
, cela nécessiterait pas de modifier les champs utilisés pour le calcul <=>.
Autres conseils
C'est exact, il peut causer des difficultés à localiser l'entrée de la carte. Officiellement, le comportement est indéfini, donc si vous l'ajoutez à un HashSet ou comme une clé dans une table de hachage, vous ne devriez pas le changer.
Oui, qui va provoquer de mauvaises choses se passent.
// Given that the Toy class has a mutable field called 'name' which is used
// in equals() and hashCode():
Set<Toy> toys = new HashSet<Toy>();
Toy toy = new Toy("Fire engine", ToyType.WHEELED_VEHICLE, Color.RED);
toys.add(toy);
System.out.println(toys.contains(toy)); // true
toy.setName("Fast truck");
System.out.println(toys.contains(toy)); // false
Dans un HashSet / HashMap, vous peut muter un objet contenu pour modifier les résultats de l'opération compareTo()
- comparaison relative n'est pas utilisé pour localiser les objets. Mais ce serait fatale dans un TreeSet / TreeMap.
Vous pouvez également muter des objets qui sont à l'intérieur d'un IdentityHashMap - rien d'autre que l'identité de l'objet est utilisé pour localiser le contenu.
Même si vous pouvez faire ces choses avec ces qualifications, ils font votre code plus fragile. Et si quelqu'un veut changer à un TreeSet plus tard, ou ajouter ce champ mutable au hashCode / test d'égalité?