HashSet Fehlerhaft

javimka

Top Contributor
Hallo zusammen,

ich habe ein seltsames Verhalten der Klasse HashSet festgestellt. Ich habe ein Objekt, welches ich in das HashSet einfüge, dann etwas verändere und nochmals einfüge. Das genau gleiche Objekt ist dann zwei mal im Set.

Gemäss Dokumentation dürfte das aber nicht passieren:
Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if this set contains no element e2 such that (e==null ? e2==null : e.equals(e2))

Ich habe ein kleines Beispiel, um dies zu veranschaulichen:
Java:
import java.util.HashSet;

public class HashSetTest {

	public static void main(String[] args) {

		HashSet<Element> set = new HashSet<Element>();
		Element element = new Element();
		set.add(element); // füge einmal ein
		element.value = 7;
		set.add(element); // füge das gleiche Objekt nochmals ein

		System.out.println("Anzahl elemente: "+set.size());
		for (Element e:set) {
			System.out.println("Element mit value "+e.value);
		}
		
		Object[] os = set.toArray();
		System.out.println("hash stimmt überein:   "+(os[0]==os[1]));
		System.out.println("equals ergibt:   "+os[0].equals(os[1]));
		System.out.println("(e==null ? e2==null : e.equals(e2)) egibt:   "+(os[0]==null ? os[1]==null : os[0].equals(os[1])));
	}
	
	static class Element extends Object {
		
		public int value = 5;
		
		@Override
		public boolean equals(Object o) {
			if (o == this) return true;
			if (!(o instanceof Element))
				return false;
			return ((Element) o).value == value;
		}
		
		@Override
		public int hashCode() {
			return value;
		}
	}
}

Die Ausgabe meines Programms zeigt klar, dass die beiden Objekte im Set, die ja genau das gleiche Objekt sind, logischerweise sowohl gleiche Hashwerte haben, als auch equals true ergibt und auch der Term aus der Dokumentation true ergibt, obwohl versprochen wird, dass das Element genau dann nicht eingefügt wird. Eine fehlerhafte Implementation wie ich finde.

Das Problem entsteht offenbar, weil sich der hashCode des Objekts zwischen den Einfügungen verändert hat. Meiner Meinung nach, müsste HashSet (insbesondere gemäss Dokumentation) dies bemerken.

Sehe ich das richtig, dass die Implementierung hier der Dokumentation nicht entspricht, könnte man dies gar einen Bug nennen? Gibt es eine kluge Alternative oder muss ich mein HashSet selber implementieren?

Dankre für alle Ideen
Gruss
Martin
 

XHelp

Top Contributor
Sehe ich das richtig, dass die Implementierung hier der Dokumentation nicht entspricht, könnte man dies gar einen Bug nennen? Gibt es eine kluge Alternative oder muss ich mein HashSet selber implementieren?

Nö. Ich verstehe nicht so ganz das Problem. In der equals-Methode vergleichst du (u.A.) den value-Wert. Zuerst ist es 5, dann ist es 7. Warum sollen diese Objekte plötzlich gleich sein? ???:L
 

Marco13

Top Contributor
Solange ein Objekt in einer HashSet liegt, darf NICHTS an dem Objekt verändert werden, was bewirken würde, dass sein hashCode sich ändert. Wenn man das doch macht, kommt eben solcher Unfug raus. Aber zugegeben: Ich wüßte jetzt nicht auswendig, wo das explizit dokumentiert ist... kann man wohl nur "wissen", wenn man eine ungefähre Vorstellung davon hat, wie eine HashSet funktioniert....
 

nrg

Top Contributor
naja. ich verstehe das Verhalten ehrlich gesagt auf den ersten Blick auch nicht ganz. Ändert man den Code z.B. wie folgt:

(plus der Implementierung von Element (s. Code TO))
Java:
        HashSet<Element> set = new HashSet<Element>();
        Element element = new Element();
        set.add(element); // füge einmal ein
        element.value = 7;
        Object[] o = set.toArray();
        System.out.println(o[0] == element);
        System.out.println(o[0].equals(element));
        System.out.println(set.contains(element));

ist die Ausgabe

Code:
true
true
false

warum ist das einzige Element des Sets inhaltlich und referenziell identisch mit [c]element[/c] aber contains liefert trotzdem false?
 
Zuletzt bearbeitet:

Marco13

Top Contributor
Weil auf basis des (geänderten) hashCodes das Bin gesucht wird, wo das Objekt reingehasht sein müßte - da ist es aber nicht drin, weil es vorher (mit dem alten hashCode) in ein anderes Bin reingehasht wurde.
 

javimka

Top Contributor
Vielen Dank Marco, dann ist das etwas versteckt wohl doch dokumentiert. Wenn das Verhalten für mutable Objekte nicht definiert ist, muss man sich wohl damit abfinden.

Nun, falls wieder mal jemand diesem Problem begegnet und eine simple Lösung sucht, die zwar vergleichsweise viel langsamer dafür aber funktionstüchtig ist, kann man das z.B. so implementieren:

Java:
public class SimpleSet<E> extends LinkedList<E> implements Set<E> {

	@Override
	public boolean add(E e) {
		boolean doit = !contains(e);
		if (doit)
			super.add(e);
		return doit;
	}
}

//edit:
@XHelp
Das Objekt ist trozdem dasselbe, auch wenn sich der gespeicherte int Wert darin verändert hat. Dem HashSet fällt dies nicht auf, weil beim zweiten Einfügen wegen dem geänderten Hashwert nicht an dem "Ort" im HashSet nach einem gleichen Objekt gesucht wird, wo es zuvor mit dem alten Hashwert eingefügt wurde.
 
Zuletzt bearbeitet:

tagedieb

Top Contributor
Wenn du Objekte in einem Set mutiertst kannst dies zu ungueltigen Zustaenden fuehren. Wenn du 2 verschiedene Instanzen in einer Set hast und setzt den Wert der 2. Instanz gleich dem der 1. Instanz erhaelst du dadurch doppelte Eintraege in einer Set --> Illegal

Set: A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element. As implied by its name, this interface models the mathematical set abstraction.

Am sichersten waere das Objekt aus der Set rausnehmen, mutieren und wieder reinstellen.
1. set.remove(object);
2. object.setValue(value);
3. set.add(onject);
 
C

cyau

Gast
es wurde schon erwähnt, aber nochmal zur verdeutlichung: aus der doku zu hashCode() in Object:

The general contract of hashCode is:
•Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
...

das ist einzuhalten, sonst macht man crap code ;)
 

tagedieb

Top Contributor
das ist einzuhalten, sonst macht man crap code

Damit bin ich einverstanden. Aber fuer mutierbare Klassen funktioniert es nicht so einfach wie es da steht.
Wenn sich alle Werte aendern koennen kann man auch keinen Hashcode berechnen. Das Einzige was uebrig bleibt ist denselben Hashcode fuer jede Instanz zu verwenden. Soweit funktioniert dies auch, sind aber zum speichern in einem HashSet definitiv ungeeignet, da alle Instanzen im selben Bucket gespeichert werden.

Java:
        public int hashCode() {
            return 735917;
        }
 

Marco13

Top Contributor
@cyau: Das hatte ich auch gefunden, es hat aber mit dem Problem nichts zu tun ;) (Oder nur indirekt). Es ist durchaus erlaubt, dass sich der HashCode eines Objektes ändert. Sofern sich das Verhalten von "equals" dazu passend ändert, ist das alles OK. Equals darf sich aber nicht ändern, wenn das Objekt in einem Set liegt. Es gilt
(1) a.equals(b) -> a.hashCode() == b.hashCode();
das kann man umformen zu
(2) !(a.hashCode() == b.hashCode()) -> !a.equals(b)
Und zusammen mit der Information aus der Set-Doku
(3) equalsChanges -> setContainsCrap
kann man daraus ableiten
hashCodeChanges --(2)--> equalsChanges --(3)--> setContainsCrap
Oder kurz: Der hashCode darf sich nicht ändern, wenn das Objekt in einer Set liegt.

Die vermutlich sinnvollste Abhilfe ist, wie tagdieb schon geschrieben hat, ein Entfernen-Ändern-NeuEinfügen. Das ist aber u.U. schwer (bis umnöglich) "global" anzuwenden, da man ja nie weiß, wer wo in einer (ggf. privaten) HashMap das Objekt gespeichert hat...
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
J HashSet mit Comparable sortieren Java Basics - Anfänger-Themen 13
berserkerdq2 Geht collections.sort bei allen? Linkedhashset, ArrayList, HashSet etc. Java Basics - Anfänger-Themen 4
volcanos HashSet und Iterator -> Falsche Sortierreihenfolge ? Java Basics - Anfänger-Themen 18
D Erste Schritte Code verstehen - HashSet Java Basics - Anfänger-Themen 8
J Hashset Java Basics - Anfänger-Themen 13
J HashSet Methode contains liefert false (hash Methode überschrieben) Java Basics - Anfänger-Themen 3
W Element aus HashSet in String umformen Java Basics - Anfänger-Themen 7
T HashSet in List-Object Java Basics - Anfänger-Themen 5
C Auf einzelne Werte aus HashSet zugreifen Java Basics - Anfänger-Themen 10
J Klassen HashSet, TreeSet: unregelmäßige Zahlenreihen beim Befüllen Java Basics - Anfänger-Themen 7
T Methoden HashSet Objekt mit Zufallszahlen befüllen Java Basics - Anfänger-Themen 3
J Verstehe meine HashSet Ausgabe nicht Java Basics - Anfänger-Themen 5
W Verknüpfung von Räumen mit Hashset Java Basics - Anfänger-Themen 10
J HashSet contain Methode funktioniert nicht wie gewollt Java Basics - Anfänger-Themen 7
M Collections HashSet verständnisproblem Java Basics - Anfänger-Themen 9
R Hashset.add(Array) liefert immer true? Java Basics - Anfänger-Themen 23
Mrtwomoon Collections Hashset elemente ohne Eckigeklammer ausgeben Java Basics - Anfänger-Themen 9
M Collections Problem bei Überschreibung von hashcode() und equals() bei Hashset-Implementierung Java Basics - Anfänger-Themen 5
A Elemente in HashSet enthalten oder nicht Java Basics - Anfänger-Themen 6
A HashSet (oder besser geignetes) Java Basics - Anfänger-Themen 14
T Hashset - Allgemeine Fragen Java Basics - Anfänger-Themen 19
J So ähnlich wie HashSet Java Basics - Anfänger-Themen 2
D HashSet vs Liste Java Basics - Anfänger-Themen 5
T HashSet Java Basics - Anfänger-Themen 3
F suche Elemente in HashSet Java Basics - Anfänger-Themen 5
E Collections HashSet - Ausgabe sortiert? Java Basics - Anfänger-Themen 3
J HashSet Implementierung Java Basics - Anfänger-Themen 16
D Problem mit HashSet Java Basics - Anfänger-Themen 12
darekkay Datentypen HashSet bzw. LinkedList mit Werten initialisieren Java Basics - Anfänger-Themen 3
B Hashset iterieren problem Java Basics - Anfänger-Themen 3
C HashSet Problem Java Basics - Anfänger-Themen 3
DasBrot Datentypen HashSet contains() Java Basics - Anfänger-Themen 3
F HashSet u. LinkedHashSet Zugriff auf Werte? Java Basics - Anfänger-Themen 2
F HashSet und LinkedHashSet Instanzierung warum so? Java Basics - Anfänger-Themen 7
M HashSet.contains() Java Basics - Anfänger-Themen 2
N Map<String, HashSet<String>> Umwandeln in Map<String, ArrayList<String>> Java Basics - Anfänger-Themen 14
neurox Limit bei HashSet? Java Basics - Anfänger-Themen 2
Povlsen84 HashSet mit eigenen Datentypen Java Basics - Anfänger-Themen 6
G HashSet vs. TreeSet Java Basics - Anfänger-Themen 3
G hashset überschreibt werte bei add Java Basics - Anfänger-Themen 1
G Wie mach ich ein HashSet für eigene Objecte? Java Basics - Anfänger-Themen 9
M HashSet Initialisierungsgröße? Java Basics - Anfänger-Themen 5
F doppelte Elemente in HashSet Java Basics - Anfänger-Themen 5
G Probleme mit HashSet Java Basics - Anfänger-Themen 5
S HashSet in HashMap, Zugriff Java Basics - Anfänger-Themen 3
G Zahlen aus HashSet in ein int Array übergeben Java Basics - Anfänger-Themen 15
G Hashset verknüpfen mit BufferedReader Java Basics - Anfänger-Themen 18
L Was ist ein HashSet? Java Basics - Anfänger-Themen 33
G HashSet Java Basics - Anfänger-Themen 21
P HashSet und Referenzen Java Basics - Anfänger-Themen 9
B Warum hat HashSet kein get(Object o) ? Java Basics - Anfänger-Themen 8
H umwandeln zu Hashset ?! Java Basics - Anfänger-Themen 7
T BMI Rechner fehlerhaft Java Basics - Anfänger-Themen 18
I InputStream beim zweiten Mal fehlerhaft Java Basics - Anfänger-Themen 10
E Warum lässt sich eine Klasse nicht starten, wenn eine andere Klasse in dem Modul fehlerhaft ist? Java Basics - Anfänger-Themen 1
U Ausgabe von Dateiinhalt während Programmnutzung fehlerhaft Java Basics - Anfänger-Themen 3
N Erste Schritte Quellcode fehlerhaft Java Basics - Anfänger-Themen 2
F (Eclipse) Beim importieren etwas fehlerhaft bitte um Hilfe :) Java Basics - Anfänger-Themen 3
P Compiler-Fehler if Bedingung fehlerhaft Java Basics - Anfänger-Themen 7
S Simple Rechnung ist fehlerhaft! Java Basics - Anfänger-Themen 13
D kleiner Taschenrechner mit switch fehlerhaft Java Basics - Anfänger-Themen 1
T Unit tests fehlerhaft bitte um hiiiiilfe :D Java Basics - Anfänger-Themen 1
M Double-Ausgabe in JTextField fehlerhaft Java Basics - Anfänger-Themen 2
H Compiler-Fehler NullPointerException bei .length, Konstruktor fehlerhaft? Java Basics - Anfänger-Themen 3
S Methode prüft fehlerhaft nach Eingangsvariable Java Basics - Anfänger-Themen 2
C FileWriter bzw. Reader fehlerhaft Java Basics - Anfänger-Themen 6
D Code Fehlerhaft finde den Fehler nicht. Java Basics - Anfänger-Themen 1
W Erste Schritte Ausgabe ist Fehlerhaft Java Basics - Anfänger-Themen 6
F Erste Schritte IF-Abfrage fehlerhaft Java Basics - Anfänger-Themen 9
Z array ausgabe fehlerhaft Java Basics - Anfänger-Themen 2
U Array Ausgabe fehlerhaft Java Basics - Anfänger-Themen 6
S Java SDK Installiert aber fehlerhaft Java Basics - Anfänger-Themen 5
N Bit -> Datei -> Bit Fehlerhaft Java Basics - Anfänger-Themen 2
H Methoden werden als fehlerhaft angezeigt Java Basics - Anfänger-Themen 2
M if/while, Ausgabe fehlerhaft! Java Basics - Anfänger-Themen 11
G Key Listener arbeitet Fehlerhaft Java Basics - Anfänger-Themen 6
S wsdl-datei: Generierung fehlerhaft Java Basics - Anfänger-Themen 9
D ClassLoader in der Jar fehlerhaft Java Basics - Anfänger-Themen 3
J Addition von double values fehlerhaft? Java Basics - Anfänger-Themen 5
S Calendar.set Time, Ermittlung der Anzahl Tage fehlerhaft Java Basics - Anfänger-Themen 9
T Java ist auch eine Insel: Beispielprogramm fehlerhaft? Java Basics - Anfänger-Themen 2

Ähnliche Java Themen

Neue Themen


Oben