Question
Ce bug m'a fallu un certain temps pour trouver ...
Considérez cette méthode:
public void foo(Set<Object> set)
{
Object obj=set.iterator().next();
set.remove(obj)
}
J'invoque la méthode avec un ensemble de hachage non vide, mais aucun élément sera supprimé!
Pourquoi serait-ce?
La solution
Pour un HashSet, cela peut se produire si les changements hashCode de l'objet après qu'il a été ajouté à l'ensemble. La méthode HashSet.remove () peut alors regarder dans le mauvais seau Hash et ne parviennent pas à le trouver.
Ce ne serait probablement pas se produire si vous avez Iterator.remove (), mais dans tous les cas, le stockage d'objets dans un HashSet dont hashCode peut changer est un accident qui devait arriver (comme vous avez découvert).
Autres conseils
Casse-tête? Si Object.hashCode
, Object.equals
ou le "jeu de hachage" ont été mises en œuvre de manière incorrecte (voir par exemple, java.net.URL
- utilisation URI
).
Aussi, si l'ensemble (directement ou indirectement) contient lui-même, quelque chose est susceptible de se produire étrange (exactement ce qui est la mise en œuvre et la phase de la personne à charge lune).
Quel est le type de mise en œuvre de l'ensemble et quels objets sont à l'intérieur du jeu?
- Si c'est une méthode HashSet, assurez-vous que la valeur de hashCode de l'objet () reste constant entre
set.put(...)
etset.remove(...)
. - Si c'est un TreeSet, assurez-vous que non modifications ont été apportées à l'objet qui affectent le comparateur ou la méthode de
compareTo
de l'objet de l'ensemble.
Dans les deux cas, le code entre set.put(...)
et set.remove(...)
constitue une violation du contrat défini par la mise en œuvre de classe respective. En règle générale, il est une bonne idée d'utiliser des objets immuables en tant que contenu de jeu (et les clés de la carte). De par leur nature même, ces objets ne peuvent pas être modifiés pendant qu'ils sont stockés dans un ensemble.
Si vous utilisez une autre implémentation ensemble, consultez son JavaDoc pour son contrat; mais le plus souvent soit equals
ou hashCode
doivent rester les mêmes alors que l'objet est contenu dans l'ensemble.
Au-delà du manque ';' après set.remove(obj)
, il peut se produire dans trois situations (cité javadoc).
ClassCastException - if the type of the specified element is incompatible with this set (optional). NullPointerException - if the specified element is null and this set does not support null elements (optional). UnsupportedOperationException - if the remove method is not supported by this set.
Vous pouvez aussi essayer:
public void foo(Set<Object> set)
{
Object obj=set.iterator().next();
iterator.remove();
}
Faut-il être:
public void foo(Set<Object> set)
{
Iterator i = set.iterator();
i.next();
i.remove();
}
Le bug pourrait être quelque chose à voir avec:
supprimer public void ()
Le comportement d'un itérateur est Non spécifié si le sous-jacent collection est modifiée pendant que le itération est en cours de quelque manière que autrement que par appel à cette méthode.
Je ne peux pas empêcher de penser que (en partie) le problème est que l'ensemble est passé par valeur, pas de référence. Je n'ai pas beaucoup d'expérience en Java, donc je pourrais être tout à fait tort.