it-swarm.com.de

Was ist mit unveränderlich gemeint?

Dies könnte die dümmste Frage sein, die jemals gestellt wurde, aber ich denke, es ist eine totale Verwirrung für einen Neuling. 

  1. Kann jemand erklären, was mit unveränderlich gemeint ist? 
  2. Warum ist eine String unveränderlich?
  3. Was sind die Vor- und Nachteile von unveränderlichen Objekten?
  4. Warum sollte ein veränderliches Objekt wie StringBuilder gegenüber String und umgekehrt bevorzugt werden?

Ein schönes Beispiel (in Java) wird sehr geschätzt.

354
ashokgelal

Unveränderlich bedeutet, dass diese Instanz nicht mehr geändert werden kann, wenn der Konstruktor eines Objekts die Ausführung abgeschlossen hat.

Dies ist hilfreich, da Sie Verweise auf das Objekt in der Umgebung übergeben können, ohne sich Sorgen zu machen, dass jemand anderes seinen Inhalt ändert. Insbesondere beim Umgang mit Parallelität gibt es keine Sperrprobleme bei Objekten, die sich niemals ändern.

z.B.

class Foo
{
     private final String myvar;

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

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

Foo muss sich keine Sorgen machen, dass der Aufrufer von getValue() möglicherweise den Text in der Zeichenfolge ändert.

Wenn Sie sich eine ähnliche Klasse wie Foo vorstellen, jedoch mit einer StringBuilder anstelle einer String als Member, können Sie feststellen, dass ein Aufrufer von getValue() das StringBuilder-Attribut einer Foo-Instanz ändern kann.

Hüten Sie sich auch vor den verschiedenen Arten der Unveränderlichkeit, die Sie möglicherweise finden: Eric Lippert hat einen Blogartikel darüber geschrieben. Grundsätzlich können Sie Objekte haben, deren Schnittstelle unveränderlich ist, aber hinter den Kulissen der tatsächliche Status der veränderlichen Personen ist (und daher nicht sicher zwischen Threads freigegeben werden kann).

251
Douglas Leeder

Ein unveränderliches Objekt ist ein Objekt, bei dem die internen Felder (oder zumindest alle internen Felder, die sich auf ihr äußeres Verhalten auswirken) nicht geändert werden können.

Es gibt viele Vorteile für unveränderliche Zeichenfolgen:

Leistung: Führen Sie die folgende Operation aus:

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

Das zugrunde liegende C für die substring () -Methode lautet wahrscheinlich so:

// 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;
}

Beachten Sie, dass keines der Zeichen kopiert werden muss! Wenn das String-Objekt veränderbar wäre (die Zeichen könnten sich später ändern), müssten Sie alle Zeichen kopieren. Andernfalls werden Änderungen an Zeichen im Teilstring später in der anderen Zeichenfolge übernommen.

Concurrency: Wenn die interne Struktur eines unveränderlichen Objekts gültig ist, ist sie immer gültig. Es besteht keine Chance, dass verschiedene Threads einen ungültigen Status in diesem Objekt erzeugen. Daher sind unveränderliche Objekte Thread Safe.

Garbage collection: Es ist für den Garbage Collector viel einfacher, logische Entscheidungen über unveränderliche Objekte zu treffen.

Die Unveränderlichkeit hat jedoch auch Nachteile:

Performance: Warten Sie, ich dachte, Sie sagten, Leistung sei ein Vorteil der Unveränderlichkeit! Nun, manchmal, aber nicht immer. Nimm den folgenden Code:

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

Beide Zeilen ersetzen das vierte Zeichen durch den Buchstaben "a". Der zweite Code ist nicht nur lesbarer, er ist auch schneller. Schauen Sie sich an, wie Sie den zugrundeliegenden Code für foo machen müssen. Die Teilzeichenfolgen sind einfach, aber jetzt, da es bereits ein Zeichen auf Platz fünf gibt und etwas anderes auf foo verweist, kann man es nicht einfach ändern. Sie müssen die gesamte Zeichenfolge kopieren (natürlich wird ein Teil dieser Funktionalität in Funktionen im eigentlichen zugrunde liegenden C abstrahiert, der Punkt hier ist jedoch, den Code anzuzeigen, der an einer Stelle ausgeführt wird).

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));

Beachten Sie, dass concatenate zweimal heißt, was bedeutet, dass der gesamte String durchgeschleift werden muss! Vergleichen Sie dies mit dem C-Code für die bar-Operation:

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

Die veränderliche Zeichenkettenoperation ist offensichtlich viel schneller.

Schlussfolgerung: In den meisten Fällen möchten Sie eine unveränderliche Zeichenfolge. Wenn Sie jedoch viel anfügen und in eine Zeichenfolge einfügen müssen, benötigen Sie die Wandlungsfähigkeit. Wenn Sie die Vorteile der Parallelität in Bezug auf Sicherheit und Speicherbereinigung nutzen möchten, müssen Sie die veränderbaren Objekte lokal für eine Methode verwenden:

// 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();
}

Da das mutable-Objekt eine lokale Referenz ist, müssen Sie sich keine Gedanken um die Sicherheit der Parallelität machen (nur ein Thread berührt sie immer). Und da es nirgendwo anders referenziert wird, wird es nur auf dem Stack zugewiesen und daher freigegeben, sobald der Funktionsaufruf beendet ist (Sie müssen sich nicht um die Garbage Collection kümmern). Und Sie erhalten alle Leistungsvorteile von Mutabilität und Unveränderlichkeit.

78
Imagist

Eigentlich ist String nicht unveränderlich, wenn Sie die oben vorgeschlagene Wikipedia-Definition verwenden. 

Der Zustand des Strings ändert die Nachkonstruktion. Schauen Sie sich die hashcode () -Methode an. String speichert den Hashcode-Wert in einem lokalen Feld, berechnet ihn jedoch erst beim ersten Aufruf von Hashcode (). Diese faule Auswertung des Hashcodes platziert String an einer interessanten Position als unveränderliches Objekt, dessen Status sich ändert, es kann jedoch nicht beobachtet werden, dass es sich ohne Reflexion geändert hat. 

Vielleicht sollte die Definition von unveränderlich ein Objekt sein, bei dem nicht beobachtet werden kann, dass es sich geändert hat. 

Wenn sich der Zustand in einem unveränderlichen Objekt ändert, nachdem es erstellt wurde, es aber niemand sehen kann (ohne Reflektion), ist das Objekt immer noch unveränderlich?

30
Gary Frost

Unveränderliche Objekte sind Objekte, die nicht programmgesteuert geändert werden können. Sie eignen sich besonders für Umgebungen mit mehreren Threads oder andere Umgebungen, in denen mehr als ein Prozess die Werte in einem Objekt ändern (mutieren) kann.

Um dies zu verdeutlichen, handelt es sich bei StringBuilder tatsächlich um ein veränderliches Objekt, nicht um ein unveränderliches. Ein regulärer Java String ist unveränderlich (dh, dass Sie den zugrunde liegenden String nach seiner Erstellung nicht mehr ändern können, ohne das Objekt zu ändern).

Angenommen, ich habe eine Klasse namens ColoredString mit einem String-Wert und einer String-Farbe:

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 diesem Beispiel wird die ColoredString-Klasse als veränderbar bezeichnet, da Sie eine ihrer Schlüsseleigenschaften ändern (mutieren) können, ohne eine neue ColoredString-Klasse zu erstellen. Der Grund, warum dies schlecht sein kann, ist beispielsweise, dass Sie eine GUI-Anwendung mit mehreren Threads haben und ColoredStrings verwenden, um Daten in das Fenster zu drucken. Wenn Sie eine Instanz von ColoredString haben, die als erstellt wurde

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

Dann würden Sie erwarten, dass die Zeichenfolge immer "Blau" ist. Wenn jedoch ein anderer Thread auf diese Instanz zugreift und aufruft

blueString.setColor("Red");

Sie würden plötzlich und wahrscheinlich unerwartet eine "rote" Saite haben, wenn Sie eine "blaue" wollten. Aus diesem Grund werden unveränderliche Objekte fast immer bevorzugt, wenn Instanzen von Objekten weitergegeben werden. Wenn Sie den Fall haben, dass veränderbare Objekte wirklich erforderlich sind, schützen Sie das Objekt in der Regel, indem Sie nur Kopien aus Ihrem spezifischen Kontrollbereich herausgeben.

In Java ist Java.lang.String ein unveränderliches Objekt (es kann nicht geändert werden, sobald es erstellt wurde), und Java.lang.StringBuilder ist ein veränderbares Objekt, da es geändert werden kann, ohne ein neues zu erstellen Beispiel.

24
Jason Coco
  1. In großen Anwendungen ist es üblich, dass String-Literale große Speicherbits beanspruchen. Um den Speicher effizient zu verarbeiten, weist die JVM einen Bereich mit dem Namen "String-Konstantenpool" zu ( Beachten Sie, dass selbst ein nicht referenzierter String im Speicher ein char [], ein int für seine Länge und ein anderes für seinen Hash-Code enthält. Für eine Zahl, im Gegensatz dazu sind maximal acht direkte Bytes erforderlich )
  2. Wenn Complier auf ein String-Literal stößt, prüft es den Pool, um festzustellen, ob bereits ein identisches Literal vorhanden ist. Wenn eines gefunden wird, wird der Verweis auf das neue Literal an den vorhandenen String gerichtet, und es wird kein neues 'String-Literal-Objekt' erstellt (der vorhandene String erhält lediglich einen zusätzlichen Verweis). 
  3. Also: String Mutability spart Speicher ...
  4. Wenn jedoch eine der Variablen ihren Wert ändert, Tatsächlich wird nur deren Referenz geändert, nicht der Wert im Speicher (daher hat dies keine Auswirkungen auf die anderen Variablen, auf die sich diese bezieht), wie unten dargestellt.

String s1 = "alter String"; 

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

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

String s2 = s1; 

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

s1 = "Neuer String";

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

Der ursprüngliche String "in memory" hat sich nicht geändert, aber der Die Referenzvariable wurde geändert, sodass sie sich auf die neue Zeichenfolge bezieht. Und wenn wir nicht s2 hätten, wäre "Old String" immer noch im Speicher, aber wir werden nicht darauf zugreifen können ...

22

"unveränderlich" bedeutet, dass Sie den Wert nicht ändern können. Wenn Sie über eine Instanz der String-Klasse verfügen, erstellt jede von Ihnen aufgerufene Methode, die den Wert zu ändern scheint, tatsächlich einen anderen String.

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

Um die Änderungen beizubehalten, sollten Sie Folgendes tun foo = foo.sustring (3);

Unveränderlich vs veränderlich kann lustig sein, wenn Sie mit Sammlungen arbeiten. Überlegen Sie, was passiert, wenn Sie ein veränderliches Objekt als Schlüssel für eine Map verwenden und dann den Wert ändern (Tipp: Denken Sie an equals und hashCode).

16
Georgy Bolyuba

Java.time

Es könnte etwas spät sein, aber um zu verstehen, was ein unveränderliches Objekt ist, betrachten Sie das folgende Beispiel aus der neuen Java 8 Date and Time API ( Java.time ). Wie Sie wahrscheinlich wissen, sind alle Datumsobjekte aus Java 8 immutable, so im folgenden Beispiel

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

Ausgabe:

2014-03-18

Dadurch wird dasselbe Jahr wie das ursprüngliche Datum gedruckt, da plusYears(2) ein neues Objekt zurückgibt, sodass das alte Datum unverändert bleibt, da es sich um ein unveränderliches Objekt handelt. Einmal erstellt, können Sie es nicht mehr ändern und die Datumsvariable zeigt immer noch darauf.

In diesem Codebeispiel sollte das neue Objekt erfasst und verwendet werden, das instanziiert und von diesem Aufruf an plusYears zurückgegeben wird.

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

date.toString ()… 2014-03-18

dateAfterTwoYears.toString ()… 2016-03-18

10
george

Für Objekte, die unveränderlich sind, kann der Zustand nach dem Erstellen nicht geändert werden.

Es gibt drei Hauptgründe, unveränderliche Objekte zu verwenden, wann immer dies möglich ist. Dies trägt dazu bei, die Anzahl der in Ihrem Code auftretenden Fehler zu reduzieren:

  • Wenn Sie wissen, dass der Status eines Objekts nicht durch eine andere Methode geändert werden kann, ist es viel einfacher, über die Funktionsweise Ihres Programms nachzudenken
  • Unveränderliche Objekte sind automatisch Thread-sicher (vorausgesetzt, sie werden sicher veröffentlicht), sodass sie niemals die Ursache für diese schwer zu findenden Multithreading-Fehler sind
  • Unveränderbare Objekte haben immer denselben Hash-Code, sodass sie als Schlüssel in einer HashMap (oder ähnlichem) verwendet werden können. Wenn sich der Hash-Code eines Elements in einer Hash-Tabelle ändern sollte, wäre der Tabelleneintrag effektiv verloren, da Versuche, ihn in der Tabelle zu finden, an der falschen Stelle suchen. Dies ist der Hauptgrund dafür, dass String-Objekte unveränderlich sind - sie werden häufig als HashMap-Schlüssel verwendet.

Es gibt auch einige andere Optimierungen, die Sie möglicherweise im Code vornehmen können, wenn Sie wissen, dass der Status eines Objekts unveränderlich ist (z. B. das Zwischenspeichern des berechneten Hashwerts). Dies sind jedoch Optimierungen und daher nicht annähernd so interessant.

8
Bill Michell

Ich mag die Erklärung von SCJP Sun Certified Programmer für Java 5 Study Guide .

Um Java effizienter zu machen, legt die JVM einen speziellen Speicherbereich mit der Bezeichnung "String-Konstantenpool" zur Verfügung. Wenn der Compiler auf ein String-Literal stößt, prüft der Pool, ob bereits ein identischer String vorhanden ist. Wenn eine Übereinstimmung gefunden wird, wird der Verweis auf das neue Literal an den vorhandenen String gerichtet, und es wird kein neues String-Literalobjekt erstellt. 

8
wen

Eine Bedeutung hat damit zu tun, wie der Wert im Computer gespeichert wird. Für eine .Net-Zeichenfolge bedeutet dies, dass die Zeichenfolge im Speicher nicht geändert werden kann. Wenn Sie denken, dass Sie den Wert ändern, erstellen Sie tatsächlich eine neue Zeichenfolge im Speicher und Verweisen auf die vorhandene Variable (die nur ein Zeiger auf die tatsächliche Sammlung von Zeichen an anderer Stelle ist) auf die neue Zeichenfolge. 

5
Charles Bretana
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="Hi": Ein Objekt s1 wurde mit dem Wert "Hi" erstellt.

s2=s1: Ein Objekt s2 wird mit Bezug auf das s1-Objekt erstellt.

s1="Bye": Der vorherige Wert des s1 -Objekts ändert sich nicht, da s1 den String-Typ hat und der String-Typ ein unveränderlicher Typ ist. Stattdessen erstellt der Compiler ein neues String-Objekt mit dem Wert "Bye" und s1 verweist darauf. Wenn wir hier den s2-Wert drucken, wird das Ergebnis "Hi" und nicht "Bye" sein, da s2 auf das vorherige s1 -Objekt verweist, das den Wert "Hi" hatte.

3
kokabi

Unveränderlich bedeutet einfach unveränderlich oder unveränderlich. Sobald das String-Objekt erstellt wurde, können seine Daten oder der Status nicht geändert werden

Beachten Sie das folgende Beispiel:

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  
  }  
 }  

Lassen Sie uns unter Berücksichtigung des folgenden Diagramms eine Idee erhalten,

 enter image description here

In diesem Diagramm sehen Sie ein neues Objekt, das als "Future World" erstellt wurde. Ändern Sie jedoch nicht "Future" .Because String is immutable. s, immer noch auf "Zukunft" verweisen. Wenn Sie "Future World" anrufen müssen,

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

Warum sind String-Objekte in Java unveränderlich?

Weil Java das Konzept des String-Literal verwendet. Angenommen, es gibt 5 Referenzvariablen, alle beziehen sich auf ein Objekt "Future". Wenn eine Referenzvariable den Wert des Objekts ändert, wird dies auf alle Referenzvariablen beeinflusst. Deshalb sind String-Objekte in Java unveränderlich.

3
Dev4World

Unveränderlich bedeutet, dass sich das Objekt nach der Erstellung nicht ändert. String ist unveränderlich, da Sie ihren Inhalt nicht ändern können . Zum Beispiel:

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

Im obigen Code wurde die Zeichenfolge s1 nicht geändert. Ein anderes Objekt (s2) wurde mit s1 erstellt.

3
eishay

Einmal instanziiert, kann nicht mehr geändert werden. Stellen Sie sich eine Klasse vor, von der eine Instanz als Schlüssel für eine Hashtabelle oder ähnliches verwendet werden kann. Testen Sie die bewährten Java-Methoden.

2
stjohnroe

Oracle-Dokumente sagt

Ein Objekt wird als immutable betrachtet, wenn sich sein Zustand danach nicht ändern kann ist konstruiert. Maximale Abhängigkeit von unveränderlichen Objekten ist weit verbreitet als solide Strategie zur Erstellung von einfachem, zuverlässigem Code akzeptiert.

Unveränderbare Objekte sind besonders nützlich in gleichzeitigen Anwendungen. Da sie den Status nicht ändern können, können sie nicht durch Thread .__ beschädigt werden. Interferenz oder beobachtet in einem inkonsistenten Zustand.

Ich mag diesen Satz aus dem Post

Unveränderbare Objekte erleichtern die gleichzeitige Programmierung

0
yoAlex5

Unveränderliche Objekte

Ein Objekt wird als unveränderlich betrachtet, wenn sich sein Zustand nach der Erstellung nicht ändern kann. Maximale Abhängigkeit von unveränderlichen Objekten wird allgemein als fundierte Strategie für die Erstellung von einfachem, zuverlässigem Code akzeptiert.

Unveränderbare Objekte sind besonders nützlich in gleichzeitigen Anwendungen. Da sie den Status nicht ändern können, können sie nicht durch Thread-Interferenz beschädigt oder in einem inkonsistenten Zustand beobachtet werden.

Programmierer zögern oft, unveränderliche Objekte zu verwenden, da sie sich um die Kosten für die Erstellung eines neuen Objekts kümmern, anstatt ein Objekt an Ort und Stelle zu aktualisieren. Die Auswirkungen der Objekterstellung werden oft überschätzt und können durch einige der mit unveränderlichen Objekten verbundenen Wirkungsgrade ausgeglichen werden. Dazu gehören ein verringerter Overhead aufgrund der Speicherbereinigung und die Beseitigung von Code, der zum Schutz veränderbarer Objekte vor Beschädigung erforderlich ist.

Die folgenden Unterabschnitte nehmen eine Klasse, deren Instanzen veränderbar sind, und leitet daraus eine Klasse mit unveränderlichen Instanzen ab. Dabei geben sie allgemeine Regeln für diese Art der Konvertierung an und zeigen einige Vorteile von unveränderlichen Objekten.

Quelle

0
Kamal Singh