Domanda

Questa potrebbe essere la domanda più stupida mai fatta, ma penso che sia abbastanza confusa per un principiante Java.

  1. Qualcuno può chiarire cosa si intende per immutabile ?
  2. Perché una String è immutabile?
  3. Quali sono i vantaggi / gli svantaggi degli oggetti immutabili?
  4. Perché un oggetto mutabile come StringBuilder dovrebbe essere preferito su String e viceversa?

Un bell'esempio (in Java) sarà molto apprezzato.

È stato utile?

Soluzione

Immutabile significa che una volta che il costruttore di un oggetto ha completato l'esecuzione quell'istanza non può essere modificata.

Questo è utile perché significa che puoi passare riferimenti all'oggetto in giro, senza preoccuparti che qualcun altro cambierà il suo contenuto. Soprattutto quando si tratta di concorrenza, non ci sono problemi di blocco con oggetti che non cambiano mai

per es.

class Foo
{
     private final String myvar;

     public Foo(final String initialValue)
     {
         this.myvar = initialValue;
     }

     public String getValue()
     {
         return this.myvar;
     }
}

Foo non deve preoccuparsi che il chiamante getValue () possa cambiare il testo nella stringa.

Se immagini una classe simile a Foo , ma con un StringBuilder anziché un String come membro, puoi vedere che un il chiamante a getValue () sarebbe in grado di modificare l'attributo StringBuilder di un'istanza Foo .

Attenzione anche ai diversi tipi di immutabilità che potresti trovare: Eric Lippert ha scritto a articolo di blog su questo. Fondamentalmente puoi avere oggetti la cui interfaccia è immutabile ma dietro le quinte lo stato privato mutevole reale (e quindi non può essere condiviso in modo sicuro tra thread).

Altri suggerimenti

Un oggetto immutabile è un oggetto in cui i campi interni (o almeno tutti i campi interni che influiscono sul suo comportamento esterno) non possono essere modificati.

Ci sono molti vantaggi nelle stringhe immutabili:

Rendimento: esegui la seguente operazione:

String substring = fullstring.substring(x,y);

La C sottostante per il metodo substring () è probabilmente qualcosa del genere:

// Assume string is stored like this:
struct String { char* characters; unsigned int length; };

// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
    struct String* out = malloc(sizeof(struct String));
    out->characters = in->characters + begin;
    out->length = end - begin;
    return out;
}

Nota che nessuno dei personaggi deve essere copiato! Se l'oggetto String fosse mutabile (i personaggi potrebbero cambiare in seguito) allora dovresti copiare tutti i caratteri, altrimenti cambierà in caratteri nella la sottostringa verrebbe riflessa successivamente nell'altra stringa.

Concorrenza: se la struttura interna di un oggetto immutabile è valida, sarà sempre valida. Non è possibile che thread diversi possano creare uno stato non valido all'interno di quell'oggetto. Quindi, gli oggetti immutabili sono Thread Safe .

Garbage collection: è molto più facile per il garbage collector prendere decisioni logiche su oggetti immutabili.

Tuttavia, ci sono anche aspetti negativi dell'immutabilità:

Performance: aspetta, pensavo avessi detto che la performance era un vantaggio dell'immutabilità! Beh, a volte lo è, ma non sempre. Prendi il seguente codice:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

Le due righe sostituiscono entrambe il quarto carattere con la lettera "a". Non solo il secondo pezzo di codice è più leggibile, è anche più veloce. Guarda come dovresti fare il codice sottostante per pippo. Le sottostringhe sono facili, ma ora perché c'è già un personaggio nello spazio cinque e qualcos'altro potrebbe fare riferimento a foo, non puoi semplicemente cambiarlo; devi copiare l'intera stringa (ovviamente alcune di queste funzionalità sono astratte in funzioni nella C reale sottostante, ma il punto qui è mostrare il codice che viene eseguito tutto in un unico posto).

struct String* concatenate(struct String* first, struct String* second)
{
    struct String* new = malloc(sizeof(struct String));
    new->length = first->length + second->length;

    new->characters = malloc(new->length);

    int i;

    for(i = 0; i < first->length; i++)
        new->characters[i] = first->characters[i];

    for(; i - first->length < second->length; i++)
        new->characters[i] = second->characters[i - first->length];

    return new;
}

// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));

Nota che il concatenato viene chiamato due volte , il che significa che l'intera stringa deve essere ripetuta! Confronta questo con il codice C per l'operazione bar :

bar->characters[4] = 'a';

L'operazione di stringa mutabile è ovviamente molto più veloce.

In conclusione: nella maggior parte dei casi, si desidera una stringa immutabile. Ma se devi aggiungere e inserire molte stringhe in una stringa, hai bisogno della mutabilità per la velocità. Se vuoi i vantaggi della sicurezza della concorrenza e della garbage collection, la chiave è mantenere gli oggetti mutabili locali in un metodo:

// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
    StringBuilder mutable;
    boolean first = true;

    for(int i = 0; i < strings.length; i++)
    {
        if(!first) first = false;
        else mutable.append(separator);

        mutable.append(strings[i]);
    }

    return mutable.toString();
}

Poiché l'oggetto mutable è un riferimento locale, non devi preoccuparti della sicurezza della concorrenza (solo un thread lo tocca mai). E poiché non è referenziato da nessun'altra parte, viene allocato solo nello stack, quindi viene deallocato non appena termina la chiamata di funzione (non devi preoccuparti della garbage collection). E ottieni tutti i vantaggi prestazionali sia della mutabilità che dell'immutabilità.

In realtà String non è immutabile se usi la definizione di Wikipedia suggerita sopra.

Lo stato della stringa cambia post costruzione. Dai un'occhiata al metodo hashcode (). String memorizza nella cache il valore di hashcode in un campo locale ma non lo calcola fino alla prima chiamata di hashcode (). Questa valutazione pigra dell'hashcode pone String in una posizione interessante come oggetto immutabile il cui stato cambia, ma non si può osservare che sia cambiato senza usare la riflessione.

Quindi forse la definizione di immutabile dovrebbe essere un oggetto che non si può osservare per essere cambiato.

Se lo stato cambia in un oggetto immutabile dopo che è stato creato ma nessuno può vederlo (senza riflessione) l'oggetto è ancora immutabile?

Gli oggetti immutabili sono oggetti che non possono essere modificati a livello di codice. Sono particolarmente utili per ambienti multi-thread o altri ambienti in cui più di un processo è in grado di alterare (mutare) i valori in un oggetto.

Solo per chiarire, StringBuilder è in realtà un oggetto mutabile, non immutabile. Una normale java String è immutabile (il che significa che una volta creata non è possibile modificare la stringa sottostante senza cambiare l'oggetto).

Ad esempio, supponiamo che io abbia una classe chiamata ColoredString che ha un valore String e un colore String:

public class ColoredString {

    private String color;
    private String string;

    public ColoredString(String color, String string) {
        this.color  = color;
        this.string = string;
    }

    public String getColor()  { return this.color;  }
    public String getString() { return this.string; }

    public void setColor(String newColor) {
        this.color = newColor;
    }

}

In questo esempio, si dice che ColoredString è mutabile perché è possibile modificare (mutare) una delle sue proprietà chiave senza creare una nuova classe ColoredString. Il motivo per cui questo potrebbe essere negativo è, ad esempio, supponiamo che tu abbia un'applicazione GUI che ha più thread e stai usando ColoredStrings per stampare i dati sulla finestra. Se hai un'istanza di ColoredString creata come

new ColoredString("Blue", "This is a blue string!");

Quindi ti aspetteresti che la stringa sia sempre " Blu " ;. Se un altro thread, tuttavia, ha ottenuto questa istanza e chiamato

blueString.setColor("Red");

Avresti improvvisamente, e probabilmente inaspettatamente, ora un " Rosso " stringa quando volevi un " Blu " uno. Per questo motivo, gli oggetti immutabili sono quasi sempre preferiti quando si passano istanze di oggetti. Quando hai un caso in cui gli oggetti mutabili sono davvero necessari, allora in genere proteggeresti l'objet passando solo copie dal tuo specifico campo di controllo.

Per ricapitolare, in Java, java.lang.String è un oggetto immutabile ( non può essere modificato una volta creato) e java.lang.StringBuilder è un oggetto mutabile perché può essere modificato senza creando una nuova istanza.

  1. Nelle applicazioni di grandi dimensioni è comune che i letterali di stringa occupino grandi quantità di memoria. Quindi, per gestire in modo efficiente la memoria, la JVM alloca un'area chiamata " String Constant Pool ". ( Nota che in memoria anche una stringa senza riferimento porta un char [], un int per la sua lunghezza e un altro per il suo hashCode. Per un numero, al contrario, un massimo di otto byte immediati è richiesta )
  2. Quando il complier incontra un valore letterale String, controlla il pool per vedere se esiste già un valore letterale identico. E se ne viene trovato uno, il riferimento al nuovo letterale viene indirizzato alla stringa esistente e non viene creato alcun nuovo "oggetto letterale stringa" (la stringa esistente ottiene semplicemente un riferimento aggiuntivo).
  3. Quindi: La mutabilità delle stringhe consente di risparmiare memoria ...
  4. Ma quando una qualsiasi delle variabili cambia valore, In realtà - cambia solo il loro riferimento, non il valore in memoria (quindi non influenzerà le altre variabili che lo fanno riferimento) come visto sotto ....

String s1 = " Vecchia stringa " ;;

//s1 variable, refers to string in memory
        reference                 |     MEMORY       |
        variables                 |                  |

           [s1]   --------------->|   "Old String"   |

Stringa s2 = s1;

//s2 refers to same string as s1
                                  |                  |
           [s1]   --------------->|   "Old String"   |
           [s2]   ------------------------^

s1 = " Nuova stringa " ;;

//s1 deletes reference to old string and points to the newly created one
           [s1]   -----|--------->|   "New String"   |
                       |          |                  |
                       |~~~~~~~~~X|   "Old String"   |
           [s2]   ------------------------^
  

La stringa originale 'in memoria' non è cambiata, ma il   la variabile di riferimento è stata modificata in modo che faccia riferimento alla nuova stringa.   E se non avessimo s2, "Vecchia stringa" sarebbe ancora nella memoria ma   non saremo in grado di accedervi ...

" immutabile " significa che non puoi cambiare valore. Se hai un'istanza della classe String, qualsiasi metodo che chiami e che sembra modificare il valore, creerà effettivamente un'altra stringa.

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

Per preservare le modifiche dovresti fare qualcosa del genere     foo = foo.sustring (3);

Immutabile vs mutabile può essere divertente quando lavori con le raccolte. Pensa a cosa accadrà se usi l'oggetto mutabile come chiave per la mappa e poi cambi il valore (suggerimento: pensa a equals e hashCode ).

java.time

Potrebbe essere un po 'tardi, ma per capire cos'è un oggetto immutabile, considera il seguente esempio dalla nuova API Java 8 Date and Time ( java.time ). Come probabilmente saprai, tutti gli oggetti data di Java 8 sono immutabili , quindi nel seguente esempio

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

Output:

  

2014/3/18

Viene stampato lo stesso anno della data iniziale perché plusYears (2) restituisce un nuovo oggetto in modo che la vecchia data rimanga invariata perché è un oggetto immutabile. Una volta creato, non è possibile modificarlo ulteriormente e la variabile della data indica ancora.

Quindi, quell'esempio di codice dovrebbe catturare e usare il nuovo oggetto istanziato e restituito da quella chiamata a plusYears .

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);
  

date.toString ()… 18-03-2014

     

dateAfterTwoYears.toString ()… 18-03-2016

Mi piace molto la spiegazione di Guida allo studio SCJP Sun Certified Programmer for Java 5 .

  

Per rendere Java più efficiente in termini di memoria, JVM mette da parte un'area speciale di memoria chiamata "pool di costanti di stringhe". Quando il compilatore incontra un valore letterale String, controlla il pool per vedere se esiste già una stringa identica. Se viene trovata una corrispondenza, il riferimento al nuovo valore letterale viene diretto alla stringa esistente e non viene creato alcun nuovo oggetto letterale stringa.

Gli oggetti immutabili non possono cambiare stato dopo essere stati creati.

Ci sono tre ragioni principali per usare oggetti immutabili ogni volta che puoi, e tutto ciò contribuirà a ridurre il numero di bug che introduci nel tuo codice:

  • È molto più facile ragionare su come funziona il tuo programma quando sai che lo stato di un oggetto non può essere modificato con un altro metodo
  • Gli oggetti immutabili sono automaticamente thread-safe (supponendo che siano pubblicati in modo sicuro), quindi non saranno mai la causa di quei bug multithreading difficili da individuare
  • Gli oggetti immutabili avranno sempre lo stesso codice Hash, quindi possono essere usati come chiavi in ??una HashMap (o simile). Se il codice hash di un elemento in una tabella hash dovesse cambiare, la voce della tabella verrebbe effettivamente persa, poiché i tentativi di trovarlo nella tabella finirebbero per guardare nel posto sbagliato. Questo è il motivo principale per cui gli oggetti String sono immutabili: vengono spesso utilizzati come chiavi HashMap.

Ci sono anche alcune altre ottimizzazioni che potresti essere in grado di fare nel codice quando sai che lo stato di un oggetto è immutabile, per esempio memorizzando nella cache l'hash calcolato, ma queste sono ottimizzazioni e quindi non sono così interessanti.

Un significato ha a che fare con il modo in cui il valore è archiviato nel computer, ad esempio per una stringa .Net, significa che la stringa in memoria non può essere cambiata, quando pensi di cambiarla, in effetti creando una nuova stringa in memoria e puntando la variabile esistente (che è solo un puntatore alla raccolta effettiva di caratteri da qualche altra parte) alla nuova stringa.

String s1="Hi";
String s2=s1;
s1="Bye";

System.out.println(s2); //Hi  (if String was mutable output would be: Bye)
System.out.println(s1); //Bye

s1 = " Ciao " : un oggetto s1 è stato creato con " Ciao " valore in esso.

s2 = s1 : un oggetto s2 viene creato con riferimento all'oggetto s1.

s1 = " Bye " : il valore dell'oggetto s1 precedente non cambia perché s1 ha tipo String e String type è un tipo immutabile, invece il compilatore crea un nuovo oggetto String con " Bye " valore e s1 a cui fa riferimento. qui quando stampiamo il valore s2 , il risultato sarà " Ciao " non " ciao " perché s2 faceva riferimento al precedente oggetto s1 che aveva " Ciao " valore.

Immutabile significa che una volta creato l'oggetto, nessuno dei suoi membri cambierà. String è immutabile poiché non è possibile modificarne il contenuto. Ad esempio:

String s1 = "  abc  ";
String s2 = s1.trim();

Nel codice sopra, la stringa s1 non è cambiata, un altro oggetto ( s2 ) è stato creato usando s1 .

Immutabile significa semplicemente immutabile o immodificabile. Una volta creato l'oggetto stringa, i suoi dati o lo stato non possono essere modificati

Considera l'esempio seguente,

class Testimmutablestring{  
  public static void main(String args[]){  
    String s="Future";  
    s.concat(" World");//concat() method appends the string at the end  
    System.out.println(s);//will print Future because strings are immutable objects  
  }  
 }  

Diamo un'idea considerando il diagramma qui sotto,

 inserisci qui la descrizione dell'immagine

In questo diagramma, puoi vedere il nuovo oggetto creato come " Future World " ;. Ma non cambiare " Future " ;. Perché String è immutabile . s , fare ancora riferimento a " Future " ;. Se devi chiamare " Future World " ;,

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

Perché gli oggetti stringa sono immutabili in Java?

  

Perché Java usa il concetto di stringa letterale. Supponiamo che ci siano 5 variabili di riferimento, tutte si riferiscono a un oggetto "Futuro". Se una variabile di riferimento modifica il valore dell'oggetto, sarà influenzata da tutte le variabili di riferimento. Ecco perché gli oggetti stringa sono immutabili in java.

Una volta installato, non può essere modificato. Considera una classe di cui un'istanza potrebbe essere utilizzata come chiave per una tabella hash o simile. Scopri le migliori pratiche Java.

Oggetti immutabili

Un oggetto è considerato immutabile se il suo stato non può cambiare dopo che è stato costruito. La massima dipendenza da oggetti immutabili è ampiamente accettata come una solida strategia per la creazione di codice semplice e affidabile.

Gli oggetti immutabili sono particolarmente utili in applicazioni simultanee. Poiché non possono cambiare stato, non possono essere danneggiati dall'interferenza del thread o osservati in uno stato incoerente.

I programmatori sono spesso riluttanti a impiegare oggetti immutabili, perché si preoccupano dei costi di creazione di un nuovo oggetto invece di aggiornare un oggetto sul posto. L'impatto della creazione di oggetti è spesso sopravvalutato e può essere compensato da alcune delle efficienze associate agli oggetti immutabili. Questi includono un sovraccarico ridotto a causa della garbage collection e l'eliminazione del codice necessario per proteggere gli oggetti mutabili dalla corruzione.

Le seguenti sottosezioni prendono una classe le cui istanze sono mutabili e ne derivano una classe con istanze immutabili. In tal modo, danno regole generali per questo tipo di conversione e dimostrano alcuni dei vantaggi degli oggetti immutabili.

Source

oracle docs afferma

  

Un oggetto è considerato immutabile se il suo stato non può cambiare dopo di esso   è costruito. La massima dipendenza da oggetti immutabili è ampiamente   accettato come una valida strategia per la creazione di un codice semplice e affidabile.

     

Gli oggetti immutabili sono particolarmente utili in applicazioni simultanee.   Dal momento che non possono cambiare stato, non possono essere danneggiati dal thread   interferenza o osservata in uno stato incoerente.

Mi piace questa frase dal post

  

Gli oggetti immutabili facilitano la programmazione della concorrenza

Un oggetto immutabile è quello che non puoi modificare dopo averlo creato. Un esempio tipico sono letterali stringa.

Un linguaggio di programmazione D, che diventa sempre più popolare, ha una nozione di "immutabilità". tramite "invariante" parola chiave. Leggi questo articolo di Dr.Dobb al riguardo - http: // dobbscodetalk .com / index.php? opzione = com_myblog & amp; show = Invariant-Strings.html & amp; Itemid = 29 . Spiega perfettamente il problema.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top