Java HashSet enthält Duplikate, wenn das enthaltene Element geändert wurde

8

Nehmen wir an, Sie haben eine Klasse und Sie erstellen ein HashSet, das diese Instanzen dieser Klasse speichern kann. Wenn Sie versuchen, Instanzen hinzuzufügen, die gleich sind, wird nur eine Instanz in der Auflistung beibehalten, und das ist in Ordnung.

Wenn Sie jedoch zwei verschiedene Instanzen im HashSet haben, und Sie eine davon nehmen und eine exakte Kopie der anderen erstellen (indem Sie die Felder kopieren), enthält das HashSet zwei doppelte Instanzen.

Hier ist der Code, der dies demonstriert:

%Vor%

Die Ausgabe aus dem obigen Code:

%Vor%

Gibt es eine Möglichkeit, das HashSet zu zwingen, seinen Inhalt zu validieren, damit mögliche doppelte Einträge, die wie im obigen Szenario erstellt wurden, entfernt werden?

Eine mögliche Lösung könnte darin bestehen, ein neues HashSet zu erstellen und den Inhalt von einem Hashset auf ein anderes zu kopieren, so dass das neue Hashset keine Duplikate enthält, aber diese Lösung nicht gefällt.

    
PB_MLT 28.10.2012, 23:21
quelle

6 Antworten

16

Die von Ihnen beschriebene Situation ist ungültig. Siehe Javadoc : "Das Verhalten eines Sets wird nicht angegeben, wenn Der Wert eines Objekts wird in einer Weise geändert, die Gleichheitsvergleiche beeinflusst, während das Objekt ein Element in der Menge ist. "

    
EJP 28.10.2012, 23:29
quelle
3

Um der @ EJP-Antwort hinzuzufügen, was in der Praxis passiert, wenn Sie Objekte in HashSet mutieren, um sie zu duplizieren (im Sinne des equals / hashcode -Vertrags), ist dies die Hash-Tabellen-Datenstruktur wird brechen.

  • Abhängig von den genauen Details der Mutation und dem Status der Hash-Tabelle werden eine oder beide Instanzen für die Suche unsichtbar (z. B. contains und andere Operationen). Entweder befindet es sich in der falschen Hash-Kette oder weil die andere Instanz davor in der Hash-Kette erscheint. Und es ist schwer vorherzusagen, welche Instanz sichtbar ist ... und ob sie sichtbar bleibt.

  • Wenn Sie die Menge iterieren, sind beide Instanzen immer noch ... in Verletzung des Set Vertrags vorhanden.

Natürlich ist das sehr aus der Anwendungsperspektive gebrochen.

Sie können dieses Problem vermeiden, indem Sie entweder:

  • Verwenden Sie einen unveränderlichen Typ für Ihre Set-Elemente,
  • Erstellen Sie eine Kopie der Objekte, wie Sie sie in die Menge einfügen und / oder aus der Menge ziehen,
  • Schreiben Sie Ihren Code so, dass er "weiß", die Objekte für die Dauer nicht zu ändern ...

Aus der Perspektive der Korrektheit und Robustheit ist die erste Option eindeutig am besten.

Übrigens wäre es wirklich schwierig, dies allgemein zu "reparieren". Es gibt keinen durchdringenden Mechanismus in Java, um zu wissen ... oder benachrichtigt zu werden ... dass sich ein Element geändert hat. Sie können einen solchen Mechanismus auf Klassenbasis implementieren, aber er muss explizit codiert werden (und er wird nicht billig sein). Selbst wenn Sie einen solchen Mechanismus hätten, was würden Sie tun? Offensichtlich sollte nun eines der Objekte aus dem Set entfernt werden ... aber welches?

    
Stephen C 29.10.2012 00:09
quelle
1

Sie haben Recht, und ich glaube nicht, dass es einen Schutz gegen den Fall gibt, den Sie diskutieren. Alle Sammlungen, die Hashing und Equals verwenden, unterliegen diesem Problem. Die Sammlung enthält keine Benachrichtigung darüber, dass sich das Objekt seit dem Hinzufügen zur Sammlung geändert hat. Ich denke, die Lösung, die Sie skizzieren, ist gut.

Wenn Sie mit diesem Problem so beschäftigt sind, müssen Sie vielleicht Ihre Datenstrukturen überdenken. Sie könnten zum Beispiel unveränderliche Objekte verwenden. Mit unveränderlichen Objekten hätten Sie dieses Problem nicht.

    
Martin Serrano 28.10.2012 23:35
quelle
1

HashSet erkennt nicht, dass sich die Eigenschaften seines Members ändern, nachdem das Objekt hinzugefügt wurde. Wenn dies ein Problem für Sie ist, sollten Sie in Betracht ziehen, GraphEdge unveränderlich zu machen. Zum Beispiel:

%Vor%

Wenn GraphEdge unveränderlich ist, führt das Ändern eines Werts dazu, dass eine neue Instanz zurückgegeben wird, anstatt die vorhandene Instanz zu ändern.

    
Mike Valenty 28.10.2012 23:35
quelle
-1

Objects.shashCode soll verwendet werden, um einen Hasencode unter Verwendung von Parameterobjekten zu erzeugen. Sie verwenden es als Teil der Hascode-Berechnung.

Ersetzen Sie Ihre Implementierung von hashCode durch Folgendes:

%Vor%     
Savvas Dalkitsis 28.10.2012 23:26
quelle
-1

Sie müssen die einmalige Erkennung durchführen, wenn Sie Ihre Liste iterieren. Ein neues HashSet zu erstellen, mag nicht der richtige Weg sein, aber warum sollte man das nicht versuchen ... Und vielleicht nicht mit einem HashSet anfangen ...

%Vor%     
slipperyseal 28.10.2012 23:45
quelle

Tags und Links