Die Standardimplementierung ist schwach und führt zu unnötigen Kollisionen. Stellen Sie sich a
class ListPair {
List<Integer> first;
List<Integer> second;
ListPair(List<Integer> first, List<Integer> second) {
this.first = first;
this.second = second;
}
public int hashCode() {
return Objects.hashCode(first, second);
}
...
}
Jetzt,
new ListPair(List.of(a), List.of(b, c))
und
new ListPair(List.of(b), List.of(a, c))
haben das gleiche hashCode
, nämlich 31*(a+b) + c
als Multiplikator fürList.hashCode
verwendete hier wiederverwendet wird. Kollisionen sind natürlich unvermeidlich, aber unnötige Kollisionen zu erzeugen ist einfach ... unnötig.
Es ist nichts wesentlich Kluges an der Verwendung 31
. Der Multiplikator muss ungerade sein, um Informationsverluste zu vermeiden (jeder gerade Multiplikator verliert mindestens das höchstwertige Bit, Vielfache von vier verlieren zwei usw.). Jeder ungerade Multiplikator ist verwendbar. Kleine Multiplikatoren können zu einer schnelleren Berechnung führen (die JIT kann Verschiebungen und Additionen verwenden), aber da die Multiplikation bei modernen Intel / AMD nur eine Latenz von drei Zyklen aufweist, spielt dies kaum eine Rolle. Kleine Multiplikatoren führen auch zu mehr Kollisionen bei kleinen Eingaben, was manchmal ein Problem sein kann.
Die Verwendung einer Primzahl ist sinnlos, da Primzahlen im Ring Z / (2 ** 32) keine Bedeutung haben.
Daher würde ich empfehlen, eine zufällig ausgewählte große ungerade Zahl zu verwenden (zögern Sie nicht, eine Primzahl zu nehmen). Da i86 / amd64-CPUs einen kürzeren Befehl für Operanden verwenden können, die in ein einzelnes vorzeichenbehaftetes Byte passen, gibt es für Multiplikatoren wie 109 einen winzigen Geschwindigkeitsvorteil. Nehmen Sie zur Minimierung von Kollisionen etwa 0x58a54cf5.
Die Verwendung verschiedener Multiplikatoren an verschiedenen Orten ist hilfreich, reicht jedoch wahrscheinlich nicht aus, um die zusätzliche Arbeit zu rechtfertigen.
Objects.hashCode(collection)
sollte ich eine perfekte Lösung sein!