Java HashSet contient des doublons, si le contenu de l'élément est modifié
Disons que vous avez un groupe et vous créer un HashSet qui peut stocker ces instances de cette classe. Si vous essayez d'ajouter des instances qui sont les mêmes, seul un exemplaire est conservé dans la collection, et c'est très bien.
Toutefois, si vous avez deux instances différentes dans le HashSet, et vous en prenez une et en faire une copie exacte de l'autre (en copiant les champs), le HashSet contient alors deux instances dupliquées.
Voici le code qui illustre cela:
public static void main(String[] args)
{
HashSet<GraphEdge> set = new HashSet<>();
GraphEdge edge1 = new GraphEdge(1, "a");
GraphEdge edge2 = new GraphEdge(2, "b");
GraphEdge edge3 = new GraphEdge(3, "c");
set.add(edge1);
set.add(edge2);
set.add(edge3);
edge2.setId(1);
edge2.setName("a");
for(GraphEdge edge: set)
{
System.out.println(edge.toString());
}
if(edge2.equals(edge1))
{
System.out.println("Equals");
}
else
{
System.out.println("Not Equals");
}
}
public class GraphEdge
{
private int id;
private String name;
//Constructor ...
//Getters & Setters...
public int hashCode()
{
int hash = 7;
hash = 47 * hash + this.id;
hash = 47 * hash + Objects.hashCode(this.name);
return hash;
}
public boolean equals(Object o)
{
if(o == this)
{
return true;
}
if(o instanceof GraphEdge)
{
GraphEdge anotherGraphEdge = (GraphEdge) o;
if(anotherGraphEdge.getId() == this.id && anotherGraphEdge.getName().equals(this.name))
{
return true;
}
}
return false;
}
}
La sortie du code ci-dessus:
1 a
1 a
3 c
Equals
Est-il un moyen pour forcer le HashSet de valider son contenu afin que les éventuels doublons créés que dans le scénario ci-dessus sont supprimés?
Une solution possible pourrait être de créer un nouveau HashSet et de copier le contenu d'un hashset à l'autre de sorte que le new hashset de ne pas contenir de doublons cependant je n'aime pas cette solution.
OriginalL'auteur PB_MLT | 2012-10-28
Vous devez vous connecter pour publier un commentaire.
La situation que vous décrivez n'est pas valide. Voir la Javadoc: "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."
La bonne solution est de s'en tenir au contrat de
Set
et de ne pas modifier les objets d'après leur ajout à la collection.qu'allez-vous atteindre en copiant le contenu dans new HashSet?
OriginalL'auteur user207421
À ajouter à @EJP réponse, ce qui va se passer dans la pratique, si vous muter objets dans un
HashSet
de faire des doublons (dans le sens de laequals
/hashcode
contrat), c'est que la table de hachage de la structure de données de casser.En fonction de la précision des détails de la mutation, et l'état de la table de hachage, un ou les deux instances seront invisibles de recherche (par exemple,
contains
et autres activités). Soit il est sur la mauvaise hachage de la chaîne, ou parce que l'autre instance apparaît devant lui sur la table de hachage de la chaîne. Et il est difficile de prédire qui sera visible ... et si elle restera visible.Si vous parcourez l'ensemble, les deux instances seront encore présents ... en violation de la
Set
contrat.Bien sûr, cela est très approximatif de l'application de la perspective.
Vous pouvez éviter ce problème en soit:
Du point de vue de la justesse et de la robustesse, la première option est clairement meilleur.
Par ailleurs, il serait vraiment difficile de les "fixer" ce de manière générale. Il n'est pas omniprésente mécanisme en Java pour savoir ... ou de la notification ... que certains élément a changé. Vous pouvez mettre en œuvre un tel mécanisme sur une classe par classe de base, mais il doit être encodé (et il ne sera pas bon marché). Même si vous n'avez avoir un tel mécanisme, que feriez-vous? Clairement l'un des objets doit maintenant être supprimé à partir de l'ensemble ... mais lequel?
n'a pas d'importance qui vous retirer, car ils sont"égaux" . Ce n'est pas vrai en général. Deux objets pour lesquels
equals()
retourtrue
n'a pas besoin d'être identiques. Et il pourrait être important que l'on vous déplacer. Et le mécanisme de vous poser est de nature hypothétique.Merci, j'ai eu du mal avec cela pendant des heures maintenant. Mais honnêtement, tout ce problème se produit uniquement parce que la mise en œuvre a été trop paresseux pour faire un bon HashSet plutôt que de simplement le sauvegarder par une table de hachage donc le gel de hashCode l'indexation de la date de création. Pour autant que je suis concearned ce HashSet qu'ils nous donnent est pas un HashSet, mais un ImmutableHashSet et un bon HashSet mise en œuvre est toujours manquant à partir du jdk, c'est vraiment scandaleux - il des caches!!!! wow.
Vous êtes sur votre propre. Tout le monde gère avec OK, et tous les autres de la table de hachage de la mise en œuvre que j'ai jamais vu a le même comportement, dont plusieurs que j'ai écrit moi-même. Vous n'avez pas vraiment besoin d'être en mesure de modifier la clé, et si vous ne vous devez vous informer de la table: sinon, il ne sait pas et qu'il se casse. La solution la plus simple est de supprimer / modifier insérer.
OriginalL'auteur Stephen C
Vous sont corrects et je ne pense pas qu'il n'y a aucun moyen de se protéger contre le cas dont vous parlez. Toutes les collections qui utiliser le hachage et égaux sont soumis à ce problème. La collecte a pas de notification que l'objet a changé depuis qu'il a été ajouté à la collection. Je pense que la solution que vous décrivez est bon.
Si vous êtes concerné par ce problème, vous devrez peut-être repenser vos structures de données. Vous pouvez utiliser des objets immuables, par exemple. Avec des objets immuables vous n'auriez pas ce problème.
OriginalL'auteur Martin Serrano
HashSet
n'est pas conscient de son membre, de l'évolution des propriétés après que l'objet a été ajouté. Si c'est un problème pour vous, alors vous pouvez envisager de faireGraphEdge
immuable. Par exemple:Dans le cas où
GraphEdge
est immuable, la modification d'une valeur dans le retour d'une nouvelle instance plutôt de changer l'instance existante.OriginalL'auteur Mike Valenty
Objets.hashCode est destiné à être utilisé pour générer un hascode à l'aide d'objets de paramètre. Vous utilisez en tant que partie de la hascode calcul.
Essayez de remplacer votre mise en œuvre de hashCode avec les éléments suivants:
J'ai supposé que vous étiez à l'aide de Google Collections de la bibliothèque:
google-collections.googlecode.com/svn/trunk/javadoc/com/google/...
Mais il semble que Pej réponse est la bonne, de toute façon
OriginalL'auteur Savvas Dalkitsis
Vous aurez besoin de faire la unique de détection d'un moment vous parcourez votre liste. La fabrication d'une nouvelle HashSet peut ne pas sembler la bonne façon de faire, mais pourquoi ne pas essayer cela... Et peut-être de ne pas utiliser un HashSet pour commencer...
Il est à l'aide d'un jeu comme une liste. Il a donc besoin d'utiliser le jeu correctement OU d'utiliser une liste.
Il ne veut pas d'une liste. Il veut un ensemble. Il a un jeu. Il est abuser, et puis vous vous demandez pourquoi ses éléments ne sont pas uniques. La solution est de ne pas faire pire, mais pour arrêter ce qui se passe dans la première place.
Je ne veux pas d'un jeu (comme mettre en œuvre en Java). Car il n'est pas adapté à mes besoins. Je veux une collection qui est sensible aux modifications effectuées sur l'un de ses éléments afin de ne pas permettre à tous les doublons jamais.
court de quelques fous hibernate style d'exécution d'accrochage de l'ensemble des méthodes, il n'existe aucun moyen d'une collection qui peut instantanément réagir à des changements sur son contenu. Faire ce EJP et d'autres suggèrent. Utiliser des objets immuables et utiliser le jeu correctement (ajout et suppression). Vous pourriez conclure dans une classe de votre choix afin de le rendre plus pratique à l'appelant.
OriginalL'auteur slipperyseal