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% 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 mitequals
übereinstimmt. Eine solche sortierte Menge (oder sortierte Karte) verletzt insbesondere den allgemeinen Vertrag für Menge (oder Karte), der inequals
-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.
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, wenne1.compareTo(e2) == 0
für jedese1.equals(e2)
und% co_de denselben booleschen Wert wiee1
hat % der Klassee2
.
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.