Ein Grund, warum unveränderbare Zeichenfolgen gut sind, ist, dass dies die Unicode-Unterstützung erleichtert. Modernes Unicode passt nicht mehr effizient in eine festgelegte Datenzelle, was die Eins-zu-Eins-Korrespondenz zwischen Zeichenfolgenindex und Speicheradresse aufhebt, die veränderbare Zeichenfolgen so vorteilhaft macht.
In der Vergangenheit haben die meisten westlichen Anwendungen einzelbyte-Zeichen verwendet (verschiedene auf ASCII basierende Codierungen oder EBCDIC...), sodass Sie sie effizient behandeln konnten, indem Sie Zeichenfolgen als Bytepuffer behandeln (wie in traditionellen C-Anwendungen).
Als Unicode noch recht neu war, gab es nicht viel Anforderung an etwas außerhalb der ersten 16 Bits, daher verwendete Java doppelbyte-Zeichen für seine String
s (und StringBuffer
s). Dies hat doppelt so viel Speicher verwendet und ignorierte mögliche Probleme von Unicode-Erweiterungen über 16 Bits hinaus, aber es war damals bequem.
Jetzt ist Unicode nicht mehr so neu, und obwohl die am häufigsten verwendeten Zeichen immer noch in 16 Bits passen, können Sie sich nicht wirklich damit herausreden, dass das Basic Multilingual Plane alles ist, was existiert. Wenn Sie ehrlich behaupten wollen, Unicode-Unterstützung zu haben, benötigen Sie entweder Zeichen mit variabler Länge oder sogar größere (32-Bit?) Zeichenzellen.
Bei Zeichen mit variabler Länge können Sie nicht mehr in O(1) Zeit in einen Zeichenfolgen beliebiger Länge zugreifen - ohne zusätzliche Informationen müssen Sie sich vom Anfang an bis zum N-ten Zeichen durchzählen. Dies tötet auch den Hauptvorteil der veränderbaren Zeichenpuffer: die Fähigkeit, Teile von Zeichenfolgen nahtlos an Ort und Stelle zu ändern.
Zum Glück benötigt die meisten Zeichenfolgenmanipulation diese Möglichkeit zur Änderung vor Ort tatsächlich nicht. Das Lesen, Parsen und Suchen erfolgt alle auf sequentieller, iterativer Basis, von Anfang bis Ende. Die allgemeine Suchen-und-Ersetzen war nie vor Ort, da die Ersetzungszeichenfolge nicht die gleiche Länge wie das Original haben muss.
Das Konkatenieren großer Mengen von Teilzeichenfolgen benötigt tatsächlich keine Änderung vor Ort, um effizient zu sein. Sie müssen jedoch vorsichtiger damit umgehen, da ein einfacher Konkatenationsloop (wie andere bereits angemerkt haben) leicht in O(N^2) werden kann, indem für jeden der N Teile Teilzeichenfolgen eine neue Zeichenfolge allokiert wird...
Ein Weg, um eine einfache Konkatenation zu vermeiden, besteht darin, ein veränderbares StringBuffer
oder ConcatBuffer
-Objekt bereitzustellen, das für effiziente Konkatenation ausgelegt ist. Ein anderer Weg wäre, einen unveränderbaren Zeichenfolgenkonstruktor einzuschließen, der einen Iterator in eine Sequenz von Zeichenfolgen enthält, die (effizient) konkateniert werden sollen.
Aber generell ist es möglich, eine unveränderbare Zeichenfolgenbibliothek zu schreiben, die durch Referenz effizient konkateniert. Diese Art von Zeichenfolge wird oft als "rope" oder "Schnur" bezeichnet, um anzudeuten, dass sie etwas schwerwiegender ist als die grundlegenden Zeichenfolgen, aus denen sie besteht, aber für Konkatenationszwecke ist sie viel effizienter, da sie die Daten überhaupt nicht neu kopieren muss!
Der obige Wikipedia-Link besagt, dass "rope"-Datenstrukturen O(log N) zum Konkatenieren sind, aber das wegweisende Papier "Purely Functional Data Structures" von Okasaki zeigt, wie man Konkatenation in O(1) Zeit durchführt.
4 Stimmen
Hier ist etwas Ähnliches: stackoverflow.com/questions/3407403/…
0 Stimmen
@Science_Fiction Die beste Antwort auf diese Frage bezieht sich im Allgemeinen auf Unveränderlichkeit. Aber warum Strings? Ich glaube, es hat etwas mit dem Mark-and-Sweep-Garbage-Collector zu tun.
1 Stimmen
stackoverflow.com/questions/2608493/…
0 Stimmen
Siehe auch wenn-alloziert-Python-neuen-Speicher-für-identische-Zeichenfolgen auf SO: meiner Meinung nach keine klare Antwort.