Ist es ein Fehler, dass die HashMap von Java 8 fehlerhaft funktioniert, wenn die Schlüssel Vergleichbar auf eine Weise implementieren, die nicht mit Gleichen konsistent ist?

8

Ich weiß, dass seit Java 8, wenn ein HashMap genug Hash-Kollisionen hat und die Schlüssel Comparable implementieren, es wird Verwenden Sie eine ausgeglichene Struktur anstelle einer verknüpften Liste für die Bin . Aber von dem, was ich sehen kann, tut das Comparable interface nicht erforderlich , dass compareTo() "konsistent mit equals() " ist (obwohl dies dringend empfohlen wird).

Habe ich etwas verpasst? Es sieht so aus, als ob die neue Implementierung HashMap die Anforderungen der Map Schnittstelle verletzen könnte, wenn die Schlüssel eine konforme, aber nicht empfohlene Comparable Implementierung haben.

Der folgende JUnit-Test macht dieses Verhalten auf OpenJDK 8u72 verfügbar:

%Vor%     
Tavian Barnes 02.02.2016, 22:02
quelle

3 Antworten

5

Es ist kein Bug irgendwo IMO, in dem der Code verhält sich wie der Implementierer erwartet - aber das ist ein bekanntes Ergebnis einer ungewöhnlichen Comparable Implementierung. Aus der Comparable -Dokumentation :

  

Es wird dringend empfohlen (obwohl nicht erforderlich), dass natürliche Ordnungen konsistent mit equals sind. Dies ist so, weil sich sortierte Mengen (und sortierte Abbildungen) ohne explizite Vergleiche "merkwürdig" verhalten, wenn sie mit Elementen (oder Schlüsseln) verwendet werden, deren natürliche Anordnung nicht mit equals übereinstimmt. Eine solche sortierte Menge (oder sortierte Karte) verletzt insbesondere den allgemeinen Vertrag für Menge (oder Karte), der in equals -Methode definiert ist.

Dies ist zwar keine sortierte Menge oder Karte im normalen Sinne, aber es besteht eine eindeutige Beziehung zu dem Problem.

Ich stimme zu, dass es sich um ein mögliches Problem handelt, und zwar ein wirklich subtiles, besonders weil es in einfachen Situationen schwer zu reproduzieren ist. Ich würde Ihre Dokumentation aktualisieren, um sehr darauf aufmerksam zu machen, dass Ihre Klasse Comparable auf eine Weise implementiert, die nicht mit equals übereinstimmt, und dies speziell als potenzielles Problem bezeichnet.

    
Jon Skeet 02.02.2016, 22:16
quelle
4

Zunächst erinnern wir uns, welche Konsistenz zwischen equals und compareTo impliziert:

( Comparable )

  

Die natürliche Reihenfolge für eine Klasse C wird genau dann als mit equals bezeichnet, wenn e1.compareTo(e2) == 0 für jedes e1.equals(e2) und% co_de denselben booleschen Wert wie e1 hat % der Klasse e2 .

Ein Szenario für eine natürliche Ordnung, die nicht mit Gleichem übereinstimmt, ist also eine Ordnung, in der C trotz des% return e1.compareTo(e2) e1.equals(e2) möglicherweise null zurückgibt. Ein Beispiel wäre eine Groß- / Kleinschreibung, bei der die Groß- und Kleinschreibung nicht berücksichtigt wird. Ein anderes Beispiel ist false , das eine natürliche Anordnung hat, wobei BigDecimal null zurückgibt, aber new BigDecimal("1.0").compareTo(BigDecimal.ONE) gibt new BigDecimal("1.0").equals(BigDecimal.ONE) zurück.

Beachten Sie, dass die neue false -Implementierung diese Fälle handhabt . Die natürliche Reihenfolge hilft nur, einen Kandidaten zu finden, aber ein Kandidat wird nur als gleich angesehen, wenn die Methode HashMap equals zurückgibt. Dies bedeutet, dass, wenn Sie viele Schlüssel mit dem gleichen Hashcode haben und gemäß ihrer natürlichen Reihenfolge gleich sind, aber nicht in Bezug auf Gleichheit, Sie am Ende mit einer linearen Suche unter diesen Schlüsseln wie in der alten Implementierung enden.

Im Gegensatz dazu ist Ihr Beispiel in einem ganz anderen Fall. Ihre true -Implementierung kann anzeigen, dass zwei Objekte nicht gleich sind, obwohl compareTo test equals zurückgibt. Das würde niemals passieren mit true oder irgendeinem anderen praktischen Beispiel einer natürlichen Ordnung, die nicht mit Gleichen übereinstimmt, die ich kenne.

Ihr Fall wird von der aktuellen Implementierung nicht unterstützt, aber wie Sie vielleicht bemerkt haben, wird der Fall nur dann unterbrochen, wenn die Objekte auch denselben Hash-Code haben. Ich bezweifle, dass dieses Szenario eine praktische Relevanz hat. Alle Beispiele, die ich bisher gesehen habe, werden nur nach dem Lernen über die neue BigDecimal -Implementierung erstellt.

    
Holger 03.02.2016 11:41
quelle
1

Nein, es handelt sich um eine dokumentierte Implementierungsbeschränkung und einen Fehler in der Implementierung von Comparable.

    
EJP 02.02.2016 22:06
quelle

Tags und Links