Co znaczy niezmienny?

104

Jeśli ciąg jest niezmienny, czy to oznacza, że ​​... (załóżmy, że JavaScript)

var str = 'foo';

alert(str.substr(1)); // oo

alert(str); // foo

Czy to oznacza, że ​​wywołując metody na łańcuchu, zwróci zmodyfikowany ciąg, ale nie zmieni początkowego ciągu?

Jeśli ciąg był zmienny, czy to oznacza, że ​​drugi również alert()by zwrócił oo?

Alex
źródło

Odpowiedzi:

105

Oznacza to, że po utworzeniu instancji obiektu nie możesz zmienić jego właściwości. W pierwszym alercie nie zmieniasz foo. Tworzysz nowy ciąg. Dlatego w drugim alercie wyświetli się „foo” zamiast oo.

Czy to oznacza, że ​​wywołując metody na łańcuchu, zwróci zmodyfikowany ciąg, ale nie zmieni początkowego ciągu?

Tak. Po utworzeniu nic nie może zmienić łańcucha. Nie oznacza to, że nie możesz przypisać nowego obiektu łańcuchowego do strzmiennej. Po prostu nie możesz zmienić bieżącego obiektu, do którego odwołuje się str.

Jeśli ciąg jest zmienny, czy to oznacza, że ​​drugi alert () zwróciłby również oo?

Technicznie nie, ponieważ metoda podciągowa zwraca nowy ciąg. Zmodyfikowanie obiektu nie zmieniłoby metody. Zmienność oznacza, że ​​technicznie rzecz biorąc, można by to zrobić tak, aby podciąg zmieniał oryginalny ciąg zamiast tworzyć nowy.

kemiller2002
źródło
Ten sam ciąg, jeśli zostanie zmodyfikowany i przypisany, zaktualizuje łańcuch var str = 'foo'; str = str.substr (1)); // oo alert (str); // oo
Ashwin G
A co z poniższym kodem. Wartość ciągu jest teraz zmieniona. var name = "Santosh"; console.log (name.substr (0,2)); var name = "kumar" console.log (nazwa); Jak to wciąż jest niezmienne
Santosh,
97

Na niższym poziomie niezmienność oznacza, że ​​pamięć, w której przechowywany jest łańcuch, nie zostanie zmodyfikowana. Po utworzeniu łańcucha "foo"pewna ilość pamięci jest przydzielana do przechowywania wartości "foo". Ta pamięć nie zostanie zmieniona. Jeśli zmodyfikujesz łańcuch, powiedzmy, substr(1)tworzony jest nowy łańcuch i przydzielana jest inna część pamięci, która będzie przechowywana "oo". Teraz masz w pamięci dwa ciągi "foo"i "oo". Nawet jeśli nie zamierzasz "foo"już używać , pozostanie, dopóki nie zostanie zebrany.

Jednym z powodów, dla których operacje na strunach są stosunkowo drogie.

zamrozić
źródło
1
Liczby też są niezmienne.
RN Kushwaha
3
witam od 2015 !! „Na niższym poziomie niezmienność oznacza, że ​​pamięć, w której przechowywana jest struna, nie zostanie zmodyfikowana.” - jest to zbyt restrykcyjne i nie brzmi dobrze. Niezmienność dotyczy tylko przestrzeni użytkownika, pamięć wirtualną można zmienić zgodnie z upodobaniami alokatora pamięci, gdy tylko zostaną zachowane niezmienniki specyfikacji języka.
zerkms
2
Ta odpowiedź jest bardziej logiczna niż zaakceptowana odpowiedź.
Ejaz Karim
Ale mogę zrobić jak var str = 'foo'; mogę modyfikować za pomocą str = „bar”. Wartość str została zmodyfikowana w ten sposób, prawda?
Hacker
@Hacker Nope. Przypisałeś nowy ciąg do zmiennej wskazującej nowe miejsce w pamięci.
deceze
13

Niezmienny oznacza to, czego nie można zmienić ani zmodyfikować.

Więc kiedy przypisujesz wartość do ciągu, ta wartość jest tworzona od zera, a nie zastępowana. Tak więc za każdym razem, gdy nowa wartość jest przypisana do tego samego ciągu, tworzona jest kopia. Tak więc w rzeczywistości nigdy nie zmieniasz oryginalnej wartości.

SoftwareGeek
źródło
9

Nie jestem pewien co do JavaScript, ale w Javie ciągi znaków stanowią dodatkowy krok w kierunku niezmienności dzięki „puli stałych ciągów”. Ciągi można konstruować za pomocą literałów ciągów ( "foo") lub Stringkonstruktora klasy. Ciągi zbudowane z literałów ciągów są częścią puli stałych ciągów, a ten sam literał ciągu będzie zawsze miał ten sam adres pamięci z puli.

Przykład:

    String lit1 = "foo";
    String lit2 = "foo";
    String cons = new String("foo");

    System.out.println(lit1 == lit2);      // true
    System.out.println(lit1 == cons);      // false

    System.out.println(lit1.equals(cons)); // true

W powyższym, oba lit1i lit2są zbudowane przy użyciu tego samego literału ciągu, więc wskazują na ten sam adres pamięci; lit1 == lit2powoduje true, że są one dokładnie tym samym obiektem.

Jednak consjest konstruowany przy użyciu konstruktora klasy. Chociaż parametr jest tą samą stałą łańcuchową, konstruktor przydziela nową pamięć cons, co oznacza, że consnie jest tym samym obiektem co lit1i lit2, mimo że zawiera te same dane.

Oczywiście, ponieważ wszystkie trzy łańcuchy zawierają te same dane znakowe, użycie equalsmetody zwróci wartość true.

(Oczywiście oba typy konstrukcji strun są niezmienne)

Brian S.
źródło
3

Niezmienne oznacza, że ​​wartości nie można zmienić. Raz utworzony obiekt typu string nie może być modyfikowany jako niezmienny. Jeśli zażądasz podłańcucha ciągu, zostanie utworzony nowy ciąg z żądaną częścią.

Używanie StringBuffer podczas manipulowania ciągami zamiast tego sprawia, że ​​operacja jest bardziej wydajna, ponieważ StringBuffer przechowuje ciąg w tablicy znaków ze zmiennymi do przechowywania pojemności tablicy znaków i długości tablicy (ciąg w postaci tablicy znaków)

Nataraj
źródło
3

Podręcznikowa definicja zmienności podlega zmianom lub może podlegać zmianom. W programowaniu używamy tego słowa na oznaczenie obiektów, których stan może zmieniać się w czasie. Niezmienna wartość jest dokładnie odwrotna - po utworzeniu nigdy nie może się zmienić.

Jeśli wydaje się to dziwne, pozwolę sobie przypomnieć, że wiele wartości, których używamy przez cały czas, jest w rzeczywistości niezmiennych.

var statement = "I am an immutable value";
var otherStr = statement.slice(8, 17);

Myślę, że nikt nie będzie zaskoczony, gdy dowie się, że druga linia w żaden sposób nie zmienia ciągu w instrukcji. W rzeczywistości żadne metody łańcuchowe nie zmieniają łańcucha, na którym działają, wszystkie zwracają nowe ciągi. Powodem jest to, że łańcuchy są niezmienne - nie mogą się zmieniać, możemy tylko tworzyć nowe.

Ciągi nie są jedynymi niezmiennymi wartościami wbudowanymi w JavaScript. Liczby też są niezmienne. Czy możesz sobie w ogóle wyobrazić środowisko, w którym ocena wyrażenia 2 + 3 zmienia znaczenie liczby 2? Brzmi absurdalnie, ale robimy to cały czas z naszymi obiektami i tablicami.

ahmed khattab
źródło
2

Od strun do stosów ... prosty do zrozumienia przykład zaczerpnięty z bloga Erica Lipperta :

Znajdowanie ścieżki przy użyciu A * w C # 3.0, część druga ...

Zmienny stos, taki jak System.Collections.Generic.Stack, najwyraźniej nie jest odpowiedni. Chcemy mieć możliwość skorzystania z istniejącej ścieżki i utworzenia z niej nowych ścieżek dla wszystkich sąsiadów jej ostatniego elementu, ale umieszczenie nowego węzła na standardowym stosie modyfikuje stos. Musielibyśmy zrobić kopie stosu przed jego wypchnięciem, co jest głupie, ponieważ wtedy niepotrzebnie duplikowalibyśmy całą jego zawartość.

Niezmienne stosy nie mają tego problemu. Naciśnięcie na niezmienny stos tworzy po prostu zupełnie nowy stos, który łączy się ze starym jako jego ogon. Ponieważ stos jest niezmienny, nie ma niebezpieczeństwa, że ​​pojawi się jakiś inny kod i zepsuje zawartość ogona. Możesz nadal używać starego stosu do syta.

Aby dowiedzieć się więcej o niezmienności, przeczytaj posty Erica, zaczynając od tego:

Niezmienność w C # część pierwsza: rodzaje niezmienności

Leniel Maccaferri
źródło
Ten cytat zawiera dziwne twierdzenie - że struktura danych stosu (w rzeczywistości wiele stosów z dzieleniem ogonów) jest, jako całość, strukturą zmienną - każde naciśnięcie lub pop zmienia strukturę. Tylko pojedyncze elementy są niezmienne. W zasadzie możesz mieć taką samą strukturę, ale z elementami zmiennymi. Dzieje się tak w niektórych odmianach nieodwracalnego znalezienia związków IIRC. Niezmienność ma duże zalety, ale współdzielenie części lub całości struktury danych jako optymalizacja jest całkowicie możliwe w przypadku elementów mutable. Nawet jeśli chcesz pozornej niezmienności innych odniesień, zawsze możesz kopiować przy zapisie w razie potrzeby.
Steve314
0

Jednym ze sposobów zrozumienia tej koncepcji jest przyjrzenie się, jak javascript traktuje wszystkie obiekty, czyli przez odniesienie. Oznacza to, że po utworzeniu instancji wszystkie obiekty są zmienne , oznacza to, że można dodać obiekt z nowymi metodami i właściwościami. Ma to znaczenie, ponieważ jeśli chcesz, aby obiekt był niezmienny, nie można go zmienić po utworzeniu wystąpienia.

Stóg
źródło