Wenn der primäre Set
war ein TreeSet
(oder vielleicht einige andere NavigableSet
), dann ist es möglich, wenn Ihre Objekte unvollkommen verglichen werden, damit dies geschehen kann.
Der kritische Punkt ist, dass HashSet.contains
wie folgt aussieht:
public boolean contains(Object o) {
return map.containsKey(o);
}
und map
ist ein HashMap
und HashMap.containsKey
wie folgt aussieht:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
so verwendet er die hashCode
des Schlüssels für die Anwesenheit zu überprüfen.
A TreeSet
verwendet jedoch eine TreeMap
intern und es ist containsKey
wie folgt aussieht:
final Entry<K,V> getEntry(Object key) {
// Offload comparator-based version for sake of performance
if (comparator != null)
return getEntryUsingComparator(key);
...
So ist es ein Comparator
verwendet den Schlüssel zu finden.
So in der Zusammenfassung, wenn Ihre hashCode
Methode mit Ihrer Comparator.compareTo
Methode (zB compareTo
kehrt 1
während hashCode
gibt unterschiedliche Werte) Sie diese Art wird nicht sieht obskuren Verhalten stimmt dann.
class BadThing {
final int hash;
public BadThing(int hash) {
this.hash = hash;
}
@Override
public int hashCode() {
return hash;
}
@Override
public String toString() {
return "BadThing{" + "hash=" + hash + '}';
}
}
public void test() {
Set<BadThing> primarySet = new TreeSet<>(new Comparator<BadThing>() {
@Override
public int compare(BadThing o1, BadThing o2) {
return 1;
}
});
// Make the things.
BadThing bt1 = new BadThing(1);
primarySet.add(bt1);
BadThing bt2 = new BadThing(2);
primarySet.add(bt2);
// Make the secondary set.
Set<BadThing> secondarySet = new HashSet<>(primarySet);
// Have a poke around.
test(primarySet, bt1);
test(primarySet, bt2);
test(secondarySet, bt1);
test(secondarySet, bt2);
}
private void test(Set<BadThing> set, BadThing thing) {
System.out.println(thing + " " + (set.contains(thing) ? "is" : "NOT") + " in <" + set.getClass().getSimpleName() + ">" + set);
}
druckt
BadThing{hash=1} NOT in <TreeSet>[BadThing{hash=1}, BadThing{hash=2}]
BadThing{hash=2} NOT in <TreeSet>[BadThing{hash=1}, BadThing{hash=2}]
BadThing{hash=1} is in <HashSet>[BadThing{hash=1}, BadThing{hash=2}]
BadThing{hash=2} is in <HashSet>[BadThing{hash=1}, BadThing{hash=2}]
so, obwohl das Objekt im TreeSet
ist es nicht, es zu finden, da der Komparator nie 0
zurückgibt. Sobald es jedoch in HashSet
ist, ist alles in Ordnung, weil HashSet
hashCode
verwendet, um es zu finden, und sie verhalten sich in einer gültigen Weise.
Ich sehe keinen Beweis, dass 'clusters' ein HashSet ist. Es könnte eine andere 'contains'-Methode verwenden – njzk2