Perché String è immutabile in Java?


78

Non riuscivo a capirne la ragione. Uso sempre la classe String come altri sviluppatori, ma quando ne modifico il valore, viene creata una nuova istanza di String.

Quale potrebbe essere la ragione dell'immutabilità per la classe String in Java?

So che ci sono alcune alternative come StringBuffer o StringBuilder. È solo curiosità.


20
Tecnicamente, non è un duplicato, ma Eric Lippert dà un'ottima risposta a questa domanda qui: programmers.stackexchange.com/a/190913/33843
Heinzi

Risposte:


105

Concorrenza

Java è stato definito dall'inizio con considerazioni sulla concorrenza. Come spesso menzionato, i mutabili condivisi sono problematici. Una cosa può cambiarne un'altra dietro il dorso di un altro thread senza che quel thread ne sia consapevole.

Esistono numerosi bug C ++ multithread che sono emersi a causa di una stringa condivisa - in cui un modulo ha ritenuto che fosse sicuro cambiare quando un altro modulo nel codice aveva salvato un puntatore su di esso e si aspettava che rimanesse lo stesso.

La 'soluzione' a questo è che ogni classe crea una copia difensiva degli oggetti mutabili che gli vengono passati. Per stringhe mutabili, questo è O (n) per fare la copia. Per stringhe immutabili, fare una copia è O (1) perché non è una copia, è lo stesso oggetto che non può cambiare.

In un ambiente multithread, gli oggetti immutabili possono sempre essere condivisi in modo sicuro tra loro. Ciò comporta una riduzione complessiva dell'utilizzo della memoria e migliora la memorizzazione nella memoria cache.

Sicurezza

Molte volte le stringhe vengono inviate come argomenti ai costruttori: connessioni di rete e protocolli sono i due che vengono in mente più facilmente. Essere in grado di cambiarlo in un momento indeterminato più tardi nell'esecuzione può portare a problemi di sicurezza (la funzione pensava che si stesse collegando a una macchina, ma era deviata su un'altra, ma tutto nell'oggetto sembra che fosse collegato alla prima ... è anche la stessa stringa).

Java permette di usare la riflessione - e i parametri per questo sono stringhe. Il pericolo di passare una stringa che può essere modificata attraverso un altro metodo che riflette. Questo è molto brutto.

Chiavi per l'hash

La tabella hash è una delle strutture di dati più utilizzate. Le chiavi della struttura dei dati sono spesso stringhe. Avere stringhe immutabili significa che (come sopra) la tabella hash non ha bisogno di fare una copia della chiave hash ogni volta. Se le stringhe fossero mutabili e la tabella hash non lo facesse, sarebbe possibile che qualcosa cambi la chiave hash a distanza.

Il modo in cui funziona l'Oggetto in Java è che tutto ha una chiave hash (accessibile tramite il metodo hashCode ()). Avere una stringa immutabile significa che l'hashCode può essere memorizzato nella cache. Considerando la frequenza con cui le stringhe vengono utilizzate come chiavi di un hash, ciò fornisce un significativo incremento delle prestazioni (piuttosto che dover ricalcolare il codice hash ogni volta).

sottostringhe

Avendo la stringa immutabile, anche l'array di caratteri sottostante che supporta la struttura dei dati è immutabile. Ciò consente alcune ottimizzazioni sul substringmetodo da eseguire (non sono necessariamente eseguite, ma introduce anche la possibilità di alcune perdite di memoria).

Se fate:

String foo = "smiles";
String bar = foo.substring(1,5);

Il valore di barè 'miglio'. Tuttavia, entrambi fooe barpossono essere supportati dallo stesso array di caratteri, riducendo l'istanza di più array di caratteri o copiandolo, semplicemente usando punti di inizio e fine diversi all'interno della stringa.

pippo | | (0, 6)
    vv
    sorrisi
     ^ ^
bar | | (1, 5)

Ora, il lato negativo di ciò (la perdita di memoria) è che se uno avesse una stringa lunga 1k e prendesse la sottostringa del primo e del secondo carattere, sarebbe anche supportato dalla matrice di caratteri lunga 1k. Questo array rimarrebbe in memoria anche se la stringa originale che aveva un valore dell'intero array di caratteri fosse garbage collection.

Si può vedere questo in String da JDK 6b14 (il seguente codice proviene da una fonte GPL v2 e usato come esempio)

   public String(char value[], int offset, int count) {
       if (offset < 0) {
           throw new StringIndexOutOfBoundsException(offset);
       }
       if (count < 0) {
           throw new StringIndexOutOfBoundsException(count);
       }
       // Note: offset or count might be near -1>>>1.
       if (offset > value.length - count) {
           throw new StringIndexOutOfBoundsException(offset + count);
       }
       this.offset = 0;
       this.count = count;
       this.value = Arrays.copyOfRange(value, offset, offset+count);
   }

   // Package private constructor which shares value array for speed.
   String(int offset, int count, char value[]) {
       this.value = value;
       this.offset = offset;
       this.count = count;
   }

   public String substring(int beginIndex, int endIndex) {
       if (beginIndex < 0) {
           throw new StringIndexOutOfBoundsException(beginIndex);
       }
       if (endIndex > count) {
           throw new StringIndexOutOfBoundsException(endIndex);
       }
       if (beginIndex > endIndex) {
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
       }
       return ((beginIndex == 0) && (endIndex == count)) ? this :
           new String(offset + beginIndex, endIndex - beginIndex, value);
   }

Notare come la sottostringa utilizza il costruttore di stringhe a livello di pacchetto che non comporta alcuna copia dell'array e sarebbe molto più veloce (a scapito della possibilità di mantenerlo attorno ad alcuni array di grandi dimensioni, anche se non duplicare array di grandi dimensioni).

Si noti che il codice sopra riportato è per Java 1.6. Il modo in cui viene implementato il costruttore di sottostringa è stato modificato con Java 1.7 come documentato in Modifiche alla rappresentazione interna di String fatta in Java 1.7.0_06 - il problema derivante dalla perdita di memoria che ho menzionato sopra. Java probabilmente non è stato visto come un linguaggio con molta manipolazione delle stringhe e quindi l'incremento delle prestazioni per una sottostringa è stata una buona cosa. Ora, con enormi documenti XML archiviati in stringhe che non vengono mai raccolte, questo diventa un problema ... e quindi il passaggio al Stringnon utilizzo dello stesso array sottostante con una sottostringa, in modo che l'array di caratteri più grande possa essere raccolto più rapidamente.

Non abusare della pila

Si potrebbe passare il valore della stringa in giro invece del riferimento alla stringa immutabile per evitare problemi con la mutabilità. Tuttavia, con stringhe di grandi dimensioni, passare questo nello stack sarebbe ... offensivo per il sistema (mettere interi documenti XML come stringhe nello stack e quindi rimuoverli o continuare a passarli lungo ...).

La possibilità di deduplicazione

Certo, questa non era una motivazione iniziale per cui le stringhe dovrebbero essere immutabili, ma quando si guarda il razionale del perché le stringhe immutabili sono una buona cosa, questo è certamente qualcosa da considerare.

Chiunque abbia lavorato un po 'con Strings sa di poter succhiare la memoria. Ciò è particolarmente vero quando stai facendo cose come estrarre i dati dai database che rimangono in sospeso per un po '. Molte volte con queste punture, sono sempre la stessa stringa (una volta per ogni riga).

Molte applicazioni Java su larga scala sono attualmente strozzate nella memoria. Le misurazioni hanno dimostrato che circa il 25% del set di dati live heap Java in questi tipi di applicazioni viene utilizzato dagli oggetti String. Inoltre, circa la metà di quegli oggetti String sono duplicati, dove duplicati significa string1.equals (string2) è vero. Avere oggetti String duplicati nell'heap è essenzialmente solo uno spreco di memoria. ...

Con l'aggiornamento 8 di Java 8, JEP 192 (motivazione citata sopra) viene implementato per risolvere questo problema. Senza entrare nei dettagli di come funziona la deduplicazione delle stringhe, è essenziale che le stringhe stesse siano immutabili. Non puoi deduplicare StringBuilders perché possono cambiare e non vuoi che qualcuno cambi qualcosa da sotto di te. Le stringhe immutabili (relative a quel pool di stringhe) indicano che puoi passare attraverso e se trovi due stringhe uguali, puoi puntare un riferimento di stringa all'altro e lasciare che il garbage collector consumi quello appena inutilizzato.

Altre lingue

L'obiettivo C (che precede Java) ha NSStringe NSMutableString.

C # e .NET hanno fatto le stesse scelte di progettazione della stringa predefinita essendo immutabile.

Anche le stringhe Lua sono immutabili.

Anche Python .

Storicamente, Lisp, Scheme, Smalltalk internano la stringa e quindi la rendono immutabile. I linguaggi dinamici più moderni usano spesso le stringhe in qualche modo che richiedono che siano immutabili (potrebbe non essere una stringa , ma è immutabile).

Conclusione

Queste considerazioni di progettazione sono state fatte ancora e ancora in una moltitudine di lingue. È opinione generale che le stringhe immutabili, nonostante tutto il loro disagio, siano migliori delle alternative e portino a un codice migliore (meno bug) e agli eseguibili più veloci in generale.


3
Java fornisce stringhe mutabili e immutabili. Questa risposta descrive alcuni vantaggi prestazionali che possono essere apportati su stringhe immutabili e alcuni motivi per cui si potrebbero scegliere dati immutabili; ma non discute il motivo per cui la versione immutabile è la versione predefinita.
Billy ONeal,

3
@BillyONeal: un default sicuro e un'alternativa non sicura porta quasi sempre a sistemi più sicuri dell'approccio opposto.
Joachim Sauer,

4
@BillyONeal Se l'immutabile non fosse l'impostazione predefinita, i problemi di concorrenza, sicurezza e hash sarebbero più comuni. I progettisti del linguaggio hanno scelto (in parte in risposta a C) di creare un linguaggio in cui sono impostati i valori predefiniti per cercare di prevenire una serie di bug comuni per provare a migliorare l'efficienza del programmatore (non dovendo più preoccuparsi di questi bug). Ci sono meno bug (ovvi e nascosti) con stringhe immutabili rispetto a stringhe mutabili.

@Joachim: non pretendo diversamente.
Billy ONeal,

1
Tecnicamente, Common Lisp ha stringhe mutabili, per operazioni e simboli "simili a stringhe" con nomi immutabili per identificatori immutabili.
Vatine,

21

Ragioni che posso ricordare:

  1. La funzione String Pool senza rendere immutabile la stringa non è affatto possibile perché in caso di pool di stringhe un oggetto stringa / letterale, ad esempio "XYZ", verrà referenziato da molte variabili di riferimento, quindi se uno di essi cambia il valore, gli altri verranno automaticamente influenzati .

  2. String è stato ampiamente utilizzato come parametro per molte classi java, ad esempio per l'apertura della connessione di rete, l'apertura della connessione al database, l'apertura dei file. Se String non è immutabile, ciò comporterebbe una grave minaccia alla sicurezza.

  3. L'immutabilità consente a String di memorizzare nella cache il suo hashcode.

  4. Lo rende thread-safe.


7

1) String Pool

Il progettista Java sa che String sarà il tipo di dati più utilizzato in tutti i tipi di applicazioni Java ed è per questo che hanno voluto ottimizzare fin dall'inizio. Uno dei passi chiave in quella direzione è stata l'idea di memorizzare letterali String nel pool String. L'obiettivo era ridurre l'oggetto String temporaneo condividendoli e per condividerli, dovevano appartenere alla classe Immutable. Non è possibile condividere un oggetto mutevole con due parti sconosciute l'una all'altra. Facciamo un esempio ipotetico, in cui due variabili di riferimento puntano allo stesso oggetto String:

String s1 = "Java";
String s2 = "Java";

Ora se s1 cambia l'oggetto da "Java" a "C ++", anche la variabile di riferimento ottiene il valore s2 = "C ++", di cui non è nemmeno a conoscenza. Rendendo String immutabile, questa condivisione di String era letterale. In breve, l'idea chiave del pool di stringhe non può essere implementata senza rendere String finale o Immutabile in Java.

2) Sicurezza

Java ha un chiaro obiettivo in termini di fornire un ambiente sicuro ad ogni livello di servizio e String è fondamentale in tutto ciò che riguarda la sicurezza. String è stato ampiamente utilizzato come parametro per molte classi Java, ad es. Per aprire una connessione di rete, è possibile passare host e porta come String, per leggere file in Java è possibile passare il percorso di file e directory come String e per aprire una connessione al database, è possibile passa l'URL del database come stringa. Se String non fosse immutabile, un utente avrebbe potuto concedere l'accesso a un determinato file nel sistema, ma dopo l'autenticazione può cambiare il PERCORSO in qualcos'altro, questo potrebbe causare seri problemi di sicurezza. Allo stesso modo, durante la connessione al database o a qualsiasi altra macchina in rete, la modifica del valore String può comportare minacce alla sicurezza. Stringhe mutabili potrebbero anche causare problemi di sicurezza in Reflection,

3) Utilizzo di String in Class Loading Mechanism

Un altro motivo per rendere String finale o Immutabile è stato guidato dal fatto che era pesantemente usato nel meccanismo di caricamento della classe. Poiché String non è immutabile, un utente malintenzionato può trarre vantaggio da questo fatto e una richiesta di caricamento di classi Java standard, ad esempio java.io.Reader, può essere cambiata in classe dannosa com.unknown.DataStolenReader. Mantenendo String finale e immutabile, possiamo almeno essere sicuri che JVM stia caricando le classi corrette.

4) Vantaggi del multithreading

Poiché Concurrency e Multi-threading erano l'offerta chiave di Java, aveva molto senso pensare alla sicurezza dei thread degli oggetti String. Poiché era previsto che String fosse ampiamente utilizzato, rendendolo Immutabile significa nessuna sincronizzazione esterna, significa un codice molto più pulito che implica la condivisione di String tra più thread. Questa singola funzione rende molto più semplice la codifica della concorrenza già complicata, confusa e soggetta a errori. Poiché String è immutabile e lo condividiamo solo tra thread, risulta in un codice più leggibile.

5) Ottimizzazione e prestazioni

Ora, quando rendi immutabile una classe, sai in anticipo che, una volta creata, questa classe non cambierà. Ciò garantisce un percorso aperto per molte ottimizzazioni delle prestazioni, ad esempio la memorizzazione nella cache. Lo stesso String lo sa, non ho intenzione di cambiare, quindi String memorizza nella cache il suo hashcode. Calcola pigramente l'hashcode e, una volta creato, basta memorizzarlo nella cache. Nel mondo semplice, quando si chiama per la prima volta il metodo hashCode () di qualsiasi oggetto String, calcola il codice hash e tutte le successive chiamate a hashCode () restituiscono valori già calcolati, memorizzati nella cache. Ciò si traduce in un buon guadagno in termini di prestazioni, dato che String è molto utilizzato nelle mappe basate su hash, ad esempio Hashtable e HashMap. La memorizzazione nella cache dell'hashcode non è stata possibile senza renderla immutabile e definitiva, in quanto dipende dal contenuto di String stesso.


5

Java Virtual Machine esegue diverse ottimizzazioni relative alle operazioni sulle stringhe che non potrebbero essere eseguite diversamente. Ad esempio, se avevi una stringa con valore "Mississippi" e hai assegnato "Mississippi" .substring (0, 4) a un'altra stringa, per quanto ne sai, è stata fatta una copia dei primi quattro caratteri per rendere "Miss" . Quello che non sai è che entrambi condividono la stessa stringa originale "Mississippi" con uno come proprietario e l'altro come riferimento di quella stringa dalla posizione 0 a 4. (Il riferimento al proprietario impedisce al proprietario di essere raccolto da il garbage collector quando il proprietario esce dal campo di applicazione)

Questo è banale per una stringa piccola come "Mississippi", ma con stringhe più grandi e operazioni multiple, non dover copiare la stringa è un grande risparmio di tempo! Se le stringhe fossero mutabili, allora non potevi farlo, perché la modifica dell'originale influirebbe anche sulle "copie" della sottostringa.

Inoltre, come cita Donal, il vantaggio sarebbe fortemente appesantito dal suo svantaggio. Immagina di scrivere un programma che dipende da una libreria e di utilizzare una funzione che restituisce una stringa. Come puoi essere sicuro che quel valore rimarrà costante? Per garantire che ciò non accada, dovresti sempre produrne una copia.

E se hai due thread che condividono la stessa stringa? Non vorresti leggere una stringa che è attualmente riscritta da un altro thread, vero? La stringa dovrebbe quindi essere thread-safe, che essendo la classe comune che è, renderebbe praticamente ogni programma Java molto più lento. Altrimenti, dovresti fare una copia per ogni thread che richiede quella stringa o dovresti inserire il codice usando quella stringa in un blocco di sincronizzazione, entrambi i quali rallentano il tuo programma.

Per tutti questi motivi, questa è stata una delle prime decisioni prese per Java al fine di differenziarsi dal C ++.


Teoricamente, puoi fare una gestione del buffer multi-layer che consente la copia su mutazione se condivisa, ma è molto difficile far funzionare in modo efficiente in un ambiente multi-thread.
Donal Fellows

@DonalFellows Ho appena ipotizzato che poiché la Java Virtual Machine non è scritta in Java (ovviamente), è gestita internamente usando puntatori condivisi o qualcosa del genere.
Neil,

5

Il motivo dell'immutabilità della stringa deriva dalla coerenza con altri tipi primitivi nella lingua. Se hai un intvalore contenente 42 e aggiungi il valore 1 ad esso, non cambi il 42. Ottieni un nuovo valore, 43, che è completamente non correlato ai valori iniziali. Le primitive mutanti diverse dalle stringhe non hanno alcun senso concettuale; e come tali programmi che considerano le stringhe come immutabili sono spesso più facili da ragionare e comprendere.

Inoltre, Java fornisce davvero stringhe sia mutabili che immutabili, come vedi StringBuilder; in realtà, solo il valore predefinito è la stringa immutabile. Se vuoi trasmettere riferimenti StringBuilderovunque, sei il benvenuto a farlo. Java utilizza tipi separati ( Stringe StringBuilder) per questi concetti perché non ha il supporto per esprimere la mutabilità o la loro mancanza nel suo sistema di tipi. Nei linguaggi che supportano l'immutabilità nei loro sistemi di tipi (ad es. C ++ const), esiste spesso un singolo tipo di stringa che serve a entrambi gli scopi.

Sì, avere la stringa come immutabile consente di implementare alcune ottimizzazioni specifiche per stringhe immutabili, come l'internato, e consente di far passare i riferimenti di stringa senza sincronizzazione tra i thread. Tuttavia, questo confonde il meccanismo con l'obiettivo previsto di una lingua con un sistema di tipi semplice e coerente. Lo paragono a come tutti pensano alla raccolta dei rifiuti nel modo sbagliato; la garbage collection non è "recupero di memoria inutilizzata"; è "simulare un computer con memoria illimitata" . Le ottimizzazioni delle prestazioni discusse sono cose che vengono fatte per far sì che l'obiettivo di stringhe immutabili si comporti bene su macchine reali; non la ragione per cui tali stringhe sono immutabili in primo luogo.


@ Billy-Oneal .. Riguardo a "Se hai un int contenente il valore 42 e aggiungi il valore 1 ad esso, non cambi il 42. Ottieni un nuovo valore, 43, che non è completamente correlato all'inizio valori." Sei sicuro?
Shamit Verma,

@Shamit: Sì, ne sono sicuro. Aggiungendo da 1 a 42 si ottiene 43. Non significa che il numero 42 significhi la stessa cosa del numero 43.
Billy ONeal

@Shamit: Allo stesso modo, non puoi fare qualcosa di simile 43 = 6e aspettarti che il numero 43 significhi la stessa cosa del numero 6.
Billy ONeal

int i = 42; i = i + 1; questo codice memorizzerà 42 in memoria e quindi cambierà i valori nella stessa posizione in 43. Quindi, in realtà, la variabile "i" acquisisce un nuovo valore di 43.
Shamit Verma

@Shamit: In quel caso, sei mutato i, non 42. Considera string s = "Hello "; s += "World";. Hai modificato il valore della variabile s. Ma le corde "Hello ", "World"e "Hello World"sono immutabili.
Billy ONeal,

4

Immutabilità significa che le costanti detenute da classi che non possiedi non possono essere modificate. Le classi che non possiedi includono quelle che sono al centro dell'implementazione di Java e le stringhe che non devono essere modificate includono elementi come token di sicurezza, indirizzi di servizio, ecc. Non dovresti davvero essere in grado di modificare questo tipo di cose (e questo vale doppiamente quando si opera in modalità sandbox).

Se String non fosse immutabile, ogni volta che l'hai recuperato da un contesto che non voleva che i contenuti della stringa cambiassero sotto i suoi piedi, dovresti prenderne una copia "per ogni evenienza". Diventa molto costoso.


4
Questo stesso identico argomento si applica a qualsiasi tipo, non solo a String. Ma, per esempio, i Arrays sono comunque mutabili. Quindi, perché sono Stringimmutabili e Arrayno. E se l'immutabilità è così importante, allora perché Java rende così difficile creare e lavorare con oggetti immutabili?
Jörg W Mittag,

1
@JörgWMittag: suppongo che sia fondamentalmente una questione di quanto radicali volessero essere. Avere una stringa immutabile era piuttosto radicale, nei tempi di Java 1.0. Avere anche un framework di raccolta immutabile (principalmente o anche esclusivamente) avrebbe potuto essere troppo radicale per ottenere un ampio uso della lingua.
Joachim Sauer,

Fare un efficace framework di raccolte immutabili è abbastanza difficile da rendere performante, parlando come qualcuno che ha scritto una cosa del genere (ma non in Java). Vorrei anche totalmente avere matrici immutabili; questo mi avrebbe risparmiato un bel po 'di lavoro.
Donal Fellows

@DonalFellows: pcollections mira a fare proprio questo (non l'ho mai usato da solo).
Joachim Sauer,

3
@ JörgWMittag: ci sono persone (comunemente da una prospettiva puramente funzionale) che sostengono che tutti i tipi dovrebbero essere immutabili. Allo stesso modo, penso che se sommi tutti i problemi che uno affronta lavorando con lo stato mutabile in software parallelo e simultaneo, potresti essere d'accordo sul fatto che lavorare con oggetti immutabili è spesso molto più facile di quelli mutabili.
Steven Evers,

2

Immagina un sistema in cui accetti alcuni dati, ne verifichi la correttezza e poi li passi (per esempio, per essere memorizzati in un DB).

Supponendo che i dati siano a Stringe devono contenere almeno 5 caratteri. Il tuo metodo è simile al seguente:

public void handle(String input) {
  if (input.length() < 5) {
    throw new IllegalArgumentException();
  }
  storeInDatabase(input);
}

Ora possiamo concordare che quando storeInDatabaseviene chiamato qui, inputsi adatterà al requisito. Ma se Stringfossero mutabili, il chiamante potrebbe alterare l' inputoggetto (da un altro thread) subito dopo che è stato verificato e prima che fosse archiviato nel database . Ciò richiederebbe un buon tempismo e probabilmente non andrà bene ogni volta, ma occasionalmente, sarebbe in grado di farti memorizzare valori non validi nel database.

I tipi di dati immutabili sono una soluzione molto semplice a questo (e molti altri relativi) problemi: ogni volta che si controlla un valore, si può dipendere dal fatto che la condizione verificata sia ancora vera in seguito.


Grazie per la spiegazione. Cosa succede se chiamo il metodo handle in questo modo; handle (nuova stringa (input + "naberlan")). Immagino di poter memorizzare valori non validi nel db in questo modo.
Yfklon,

1
@blank: bene, dal momento che il inputdel handlemetodo è già troppo a lungo (non importa quale sia l' originale input è), sarebbe semplicemente un'eccezione. Stai creando un nuovo input prima di chiamare il metodo. Non è un problema.
Joachim Sauer,

0

In generale, incontrerai tipi di valore e tipi di riferimento . Con un tipo di valore, non ti interessa l'oggetto che lo rappresenta, ti preoccupi del valore. Se ti do un valore, ti aspetti che quel valore rimanga lo stesso. Non vuoi che cambi improvvisamente. Il numero 5 è un valore. Non ti aspetti che cambi improvvisamente a 6. La stringa "Hello" è un valore. Non ti aspetti che cambi improvvisamente in "P *** off".

Con i tipi di riferimento ti preoccupi per l'oggetto e ti aspetti che cambi. Ad esempio, ci si aspetta spesso che un array cambi. Se ti do un array e vuoi mantenerlo esattamente com'è, o devi fidarti di me per non cambiarlo, o ne fai una copia.

Con la classe di stringhe Java, i progettisti hanno dovuto prendere una decisione: è meglio se le stringhe si comportano come un tipo di valore o devono comportarsi come un tipo di riferimento? Nel caso delle stringhe Java, è stata presa la decisione che dovrebbero essere tipi di valore, il che significa che poiché sono oggetti, devono essere oggetti immutabili.

La decisione opposta avrebbe potuto essere presa, ma secondo me avrebbe causato molti mal di testa. Come detto altrove, molte lingue hanno preso la stessa decisione e sono giunti alla stessa conclusione. Un'eccezione è C ++, che ha una classe di stringhe e le stringhe possono essere costanti o non costanti, ma in C ++, a differenza di Java, i parametri degli oggetti possono essere passati come valori e non come riferimenti.


0

Sono davvero sorpreso che nessuno l'abbia sottolineato.

Risposta: non ti gioverebbe in modo significativo, anche se fosse mutevole. Non ti gioverebbe tanto quanto ciò causa ulteriori problemi. Esaminiamo due casi più comuni di mutazione:

Modifica di un carattere di una stringa

Poiché ogni carattere in una stringa Java richiede 2 o 4 byte, chiediti, guadagneresti qualcosa se potessi mutare la copia esistente?

Nello scenario in cui si sta sostituendo un carattere a 2 byte con uno a 4 byte (o viceversa), è necessario spostare la parte rimanente della stringa di 2 byte a sinistra o a destra. Il che non è poi così diverso dal copiare l'intera stringa dal punto di vista computazionale.

Anche questo è un comportamento davvero irregolare, generalmente indesiderato. Immagina che qualcuno stia testando un'applicazione con testo inglese e quando l'applicazione viene adottata in paesi stranieri, come la Cina, tutto inizia a esibirsi in modo strano.

Aggiunta di un'altra stringa (o carattere) a quella esistente

Se hai due stringhe arbitrarie, quelle si trovano in due posizioni di memoria distinte. Se vuoi cambiare il primo aggiungendo il secondo, non puoi semplicemente richiedere memoria aggiuntiva alla fine della prima stringa, poiché probabilmente è già occupata.

Devi copiare la stringa concatenata in una posizione completamente nuova, esattamente come se entrambe le stringhe fossero immutabili.

Se si desidera eseguire gli accodamenti in modo efficiente, è possibile che si desideri utilizzare StringBuilder, che riserva una notevole quantità di spazio alla fine di una stringa, proprio per questo scopo di una possibile aggiunta futura.


-2
  1. sono costosi e mantenerli immutabili consente cose come stringhe secondarie che condividono l'array di byte della stringa principale. (aumento della velocità anche perché non è necessario creare un nuovo array di byte e copiarlo)

  2. sicurezza - non vorrebbe rinominare il pacchetto o il codice della classe

    [rimosso il vecchio 3 guardava StringBuilder src - non condivide la memoria con la stringa (fino a quando non viene modificato) Penso che fosse in 1.3 o 1.4]

  3. hashcode della cache

  4. per stringhe mutabili usare SB (builder o buffer secondo necessità)


2
1. Naturalmente, c'è la penalità di non essere in grado di distruggere le parti più grandi della corda se ciò accade. Lo stage non è gratuito; sebbene migliori le prestazioni per molti programmi del mondo reale. 2. Potrebbero esserci facilmente "string" e "ImmutableString" che potrebbero soddisfare tale requisito. 3. Non sono sicuro di capire che ...
Billy ONeal

.3. avrebbe dovuto essere memorizzato nella cache il codice hash. Anche questo potrebbe essere fatto con una stringa mutabile. @ billy-oneal
tgkprog,

-4

Le stringhe avrebbero dovuto essere un tipo di dati primitivo in Java. Se lo fossero stati, le stringhe sarebbero automaticamente mutabili e la parola chiave finale avrebbe generato stringhe immutabili. Le stringhe mutabili sono utili e quindi ci sono più hack per stringhe mutabili nelle classi stringbuffer, stringbuilder e charsequence.


3
Questo non risponde all'aspetto "perché" di ciò che è ora che la domanda si pone. Inoltre, java final non funziona in questo modo. Le stringhe mutabili non sono hack, ma piuttosto considerazioni di progettazione reali basate sugli usi più comuni delle stringhe e sulle ottimizzazioni che possono essere fatte per migliorare il jvm.

1
La risposta al "perché" è una decisione di progettazione linguistica scadente. Tre modi leggermente diversi per supportare stringhe mutabili è un hack che il compilatore / JVM dovrebbe gestire.
CWallach,

3
String e StringBuffer erano l'originale. StringBuilder è stato aggiunto in seguito riconoscendo una difficoltà di progettazione con StringBuffer. Stringhe mutevoli e immutabili che sono oggetti diversi si trovano in molte lingue poiché la considerazione progettuale è stata fatta più volte e ha deciso che ognuno di essi è ogni volta oggetti diversi. C # "Le stringhe sono immutabili" e perché .NET String è immutabile? , obiettivo C NSString è immutabile mentre NSMutableString è mutabile. stackoverflow.com/questions/9544182
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.