Was ist schlecht an Strings als Hash-Keys?

Huhu,

Zitat aus dem aktuellen Java-Magazin:

„Bitte nutzen Sie von heute an die Methode finalize eines Objektes nicht mehr. Sollten Sie es doch getan haben, haken Sie es einfach als Jugendsünde ab, es ist auch nicht schlimmer als die Verwendung von Strings als Schlüssel einer Hash-basierten Collection …“

Ersteres lasse ich mir noch einreden (bzw. ist in dem Artikel erklärt), aber weiss jemand was dagegen spricht Strings als Schlüssel in einem Hashtable zu verwenden? :o)

Grüße, Robert

„Bitte nutzen Sie von heute an die Methode finalize eines
Objektes nicht mehr. Sollten Sie es doch getan haben, haken
Sie es einfach als Jugendsünde ab, es ist auch nicht schlimmer
als die Verwendung von Strings als Schlüssel einer
Hash-basierten Collection …“

Ersteres lasse ich mir noch einreden (bzw. ist in dem Artikel
erklärt), aber weiss jemand was dagegen spricht Strings als
Schlüssel in einem Hashtable zu verwenden? :o)

Hä? Gute Frage… eigentlich nimmt man doch zu 98% Strings bei Hashtables, meistens hat der Schlüssel ja auch irgendeinen Sinn. Ein Integer wäre vielleicht schneller ein wenig beim Berechnen des Hashkey’s aber das können die doch kaum meinen!?

Ich schreib denen mal einen Leserbrief. :o)

Grüße, Robert

Lösung
Hab eine ausführliche Antwort bekommen:

[…] die Ermittlung eines Hash-Wertes über einem String ist ein ziemlich aufwendiger Algorithmus. Gleichzeitig fragt die HashMap (oder andere Hash-basierte Collections) diesen Wert sehr oft ab. Das können Sie nachvollziehen, indem Sie einfach mal die Methode mit einem System.out überschreiben und ein wenig mit der Map experimentieren. Daher empfiehlt sich bei großen Collections (also nicht, wenn Sie mal drei UI-Elemente in einer Map speichern) das Caching dieses Wertes in einer Wrapper-Klasse, wie im Anhang zu sehen. Diese Klasse berechnet den String-Hashcode nur einmal. […]

Falls es jemand interessiert kann ich den Beispielcode auch posten.

Grüße, Robert

Ja poste mal bitte.

Und was meinst du dazu? Weiss nicht ob man da nicht zuviel Aufhebens um ein Problem, das eigentlich keins ist, macht…

Und was meinst du dazu? Weiss nicht ob man da nicht zuviel
Aufhebens um ein Problem, das eigentlich keins ist, macht…

Würde mich wundern, wenn die Java-API nicht so schlau wäre, den Hash-Wert nur beim ersten Zugriff zu berechnen und danach fest zu speichern. Strings sind ja schließlich einzig und allein aus Geschwindigkeitsgründen unveränderbar, da wäre diese kleine Optimierung doch schon sehr naheliegend, oder?

Allerdings steht in der Doku nur folgendes:

hashCode

public int hashCode()

 Returns a hashcode for this string. The hashcode for a String 
object is computed as

s[0]\*31^(n-1) + s[1]\*31^(n-2) + ... + s[n-1]
 

using int arithmetic, where s[i] is the ith character of the
string, n is the length of the string, and ^ indicates
exponentiation. (The hash value of the empty string is zero.)

Overrides:
hashCode in class Object

Returns:
a hash code value for this object.

… und ergo gar nichts zu internen Optimierungen. Wer auf Nummer Sicher gehen will, kann aber natürlich eine Unterklasse ableiten, darin hashCode() überschreiben und den Wert eben nur beim ersten Aufruf berechnen lassen (danach ein Flag setzen, aber „synchronized“ ist sicherlich hier nicht nötig).

Dann kann nix mehr schiefgehen (kostet schlimmstenfalls ein bisschen Speicherplatz, eben 1x int+boolean). Ach so, dann müssten natürlich auch noch alle Methoden überschrieben werden, die einen neuen String aus dem alten erzeugen, damit diese auch wieder ein Objekt des neuen Typs erzeugen…

Schon ein bisschen aufwändig, das „von außen“ zu machen. Hat eigentlich schon mal wer nachgemessen, ob alle Berechnungen des Hash-Wertes wirklich gleich schnell (bzw. langsam) sind? Wäre ja wohl das erste, oder? Na ja, außer vielleicht mal in den Quellcode des JDKs zu schauen natürlich, den gibt’s ja zum Download…

Gruß,
Stefan :smile:

Gerade mal reingeschaut in die Source, der Hashcode wird gecached und nur einmal berechnet.

So kompliziert ist der Algorithmus übrigens auch grad nicht (hast ihn ja schon in worten beschrieben):

public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;

for (int i = 0; i

weiss jemand was dagegen spricht Strings als
Schlüssel in einem Hashtable zu verwenden?

Hallo!
Der Übersichtlichkeit halber also hier nochmal ausführlich, was Bruno unten schon herausgefunden hat: Der Hash-Wert wird für Strings (naheliegenderweise) nur einmal berechnet und auch nur, wenn er gebraucht wird. Danach ist die Abfrage genauso schnell wie bei einem Integer-Objekt.

Dieser kleine Algorithmus ist eigentlich immer eine empfehlenswerte Vorgehensweise, um Mehrfachberechnungen zu verhindern, aber sich diese auch im Nichtbenutzungsfalle zu sparen.

Strings als Hash-Schlüsselobjekte sind in Java also effizient (und auch überaus praktisch…)

Eine Möglichkeit der äußeren Optimierung (und auch guter Stil) wäre es trotzdem vielleicht noch, konstante String-Variablen (final static) mehrfach hingeschriebenen Zeichenketten vorzuziehen, da der Compiler sonst auch für die gleiche Zeichenkette mehrere Objekte anlegen kann (muss nicht so sein) und der Geschwindigkeitsvorteil dabei flöten ginge…

Aber das ist eher Feinschliff…

Frühnächtliche Grüße,
Stefan :smile:

Hallo,

Ich glaub für die meisten Programme ist es wirklich egal.

Ich hab gerade mal ein Programm wo es eventuell einen Unterschied macht, das verwendet zwar keinen sehr grossen Hashtable (ca. 30 Elemente) fragt aber 50 - 100 Mal in der Sekunde ab.

Ich hab jetzt kurz mal einen Wrapper geschrieben der das Gegenteil macht, den Hashwert jedes Mal neu berechnet und mit einem Profiler gemessen wieviel Zeit er mit dem berechnen des Hash verbringt (innerhalb des oben genannten Programmes), das ist schon ziemlich viel geworden im Gegensatz zu vorher (Die Zahlen sind leider nicht aussagekräftig, die absoluten nicht weil der Profiler den Ablauf verlangsamt und die relativen nicht, weil das meine Programmlogik nicht miteinbezieht).

Also wenn es so wäre wie der Typ gesagt hat, dann hat er bei Programmen die unter hoher Last stehen sicher recht damit.

Aber da die String-Klasse scheinbar eh so gscheit ist, erledigt sich die Diskussion so und so. :smile:

Grüße, Robert

Ein Letztes …
… hab jetzt dem Typen nochmal gemailt, er hat gemeint, dass das eine ihm scheinbar unbekannte Neuerung von 1.3.x ist, bei 1.2.x hat er den Hash noch jedes Mal berechnet. :smile:

Grüße, Robert