44 Stimmen

HashSet.remove() und Iterator.remove() funktionieren nicht

Ich habe Probleme mit Iterator.remove() auf ein HashSet aufgerufen.

Ich habe eine Reihe von zeitgestempelten Objekten. Bevor ich dem Set ein neues Element hinzufüge, durchlaufe ich das Set, identifiziere eine alte Version dieses Datenobjekts und entferne sie (bevor ich das neue Objekt hinzufüge). Der Zeitstempel ist in hashCode und equals() enthalten, aber nicht in equalsData().

for (Iterator<DataResult> i = allResults.iterator(); i.hasNext();)
{
    DataResult oldData = i.next();
    if (data.equalsData(oldData))
    {   
        i.remove();
        break;
    }
}
allResults.add(data)

Das Seltsame ist, dass i.remove() für einige Elemente in der Menge stillschweigend fehlschlägt (keine Ausnahme). Ich habe überprüft

  • Die Zeile i.remove() wird tatsächlich aufgerufen. Ich kann sie vom Debugger aus direkt am Haltepunkt in Eclipse aufrufen und es gelingt mir immer noch nicht, den Zustand von Set

  • DataResult ist ein unveränderliches Objekt, das sich also nicht mehr ändern kann, nachdem es der Menge ursprünglich hinzugefügt wurde.

  • Die Methoden equals und hashCode() verwenden @Override, um sicherzustellen, dass es sich um die richtigen Methoden handelt. Unit-Tests überprüfen, ob sie funktionieren.

  • Dies schlägt auch fehl, wenn ich stattdessen einfach eine for-Anweisung und Set.remove verwende. (z. B. Schleife durch die Elemente, finden Sie das Element in der Liste, dann rufen Sie Set.remove(oldData) nach der Schleife).

  • Ich habe mit JDK 5 und JDK 6 getestet.

Ich dachte, ich müsste etwas Grundlegendes übersehen haben, aber nachdem wir einige Zeit damit verbracht haben, sind mein Kollege und ich ratlos. Haben Sie Vorschläge, was man überprüfen könnte?

EDIT:

Es gab Fragen - ist DataResult wirklich unveränderlich. Ja. Es gibt keine Setzer. Und wenn das Date-Objekt abgerufen wird (das ein veränderbares Objekt ist), wird eine Kopie davon erstellt.

public Date getEntryTime()
{
    return DateUtil.copyDate(entryTime);
}

public static Date copyDate(Date date)
{
    return (date == null) ? null : new Date(date.getTime());
}

FURTHER EDIT (einige Zeit später): Für das Protokoll - DataResult war nicht unveränderlich! Es verwies auf ein Objekt, das einen Hashcode hatte, der sich änderte, wenn es in der Datenbank gespeichert wurde (schlechte Praxis, ich weiß). Es stellte sich heraus, dass sich der Hashcode von DataResult änderte, wenn ein DataResult mit einem transienten Unterobjekt erstellt wurde und das Unterobjekt persistiert wurde.

Sehr subtil - ich habe mir das mehrmals angeschaut und nicht bemerkt, dass es nicht unveränderbar ist.

0 Stimmen

Zwei Möglichkeiten. 1. Sie sagen, dass DataResult unveränderlich ist. Kann man davon ausgehen, dass die Werte durch den Konstruktor gesetzt werden und es keine Set-Methoden gibt? 2. Ihre Gleichheits- und Hashcodes funktionieren nicht so, wie Sie es erwarten. Können Sie den Code für diese beiden posten?

2voto

Chris Kessel Punkte 5281

Es ist fast sicher der Fall, dass die Hashcodes für die alten und neuen Daten, die "gleich()" sind, nicht übereinstimmen. Ich habe in diese Art der Sache vor laufen und Sie im Wesentlichen am Ende spucken Hashcodes für jedes Objekt und die String-Darstellung und versuchen, herauszufinden, warum die Nichtübereinstimmung geschieht.

Wenn Sie Elemente vor/nach der Datenbank vergleichen, gehen manchmal die Nanosekunden verloren (abhängig vom Typ Ihrer DB-Spalte), was dazu führen kann, dass sich die Hashcodes ändern.

1voto

Ken Gentle Punkte 13137

Haben Sie etwas ausprobiert wie

boolean removed = allResults.remove(oldData)
if (!removed) // COMPLAIN BITTERLY!

Mit anderen Worten: Entfernen Sie das Objekt aus der Menge und unterbrechen Sie die Schleife. Das wird nicht dazu führen, dass die Iterator zu beschweren. Ich glaube nicht, dass dies eine langfristige Lösung ist, aber ich würde Ihnen wahrscheinlich einige Informationen über die hashCode , equals y equalsData Methoden

1voto

Lance Wang Punkte 1

Das Java HashSet hat ein Problem in der Methode "remove()". Prüfen Sie den Link unten. Ich habe zu TreeSet gewechselt und es funktioniert gut. Aber ich brauche die O(1) Zeitkomplexität.

https://bugs.openjdk.java.net/browse/JDK-8154740

-2voto

Zach Scrivena Punkte 28381

Wenn es zwei Einträge mit denselben Daten gibt, wird nur einer von ihnen ersetzt... haben Sie das berücksichtigt? Und haben Sie vorsichtshalber eine andere Auflistungsdatenstruktur ausprobiert, die keinen Hashcode verwendet, z. B. eine Liste?

0 Stimmen

Das ist richtig, aber Duplikate werden durch die Methode equals() definiert, die in diesem Fall die Daten + Zeitstempel verwendet.

-4voto

David Punkte 105

Ich bin nicht auf dem Laufenden über meine Java, aber ich weiß, dass Sie nicht ein Element aus einer Sammlung zu entfernen, wenn Sie über diese Sammlung in .NET iterieren, obwohl .NET wird eine Ausnahme werfen, wenn es dies fängt. Könnte dies das Problem sein?

0 Stimmen

Es kann zwar vorkommen, dass man ein Element nicht aus einer Sammlung entfernen kann, die einen Iterator unterstützt (es könnte eine ConcurrentModificationException ausgelöst werden), aber man kann das Element direkt über den Iterator selbst entfernen, wenn er diese Aktion unterstützt, was bei HashSet der Fall ist.

0 Stimmen

Und die Iteratoren, die das Entfernen überhaupt nicht unterstützen, werfen bei jedem remove()-Versuch UnsupportedOperationException.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X