不变是什么意思?


103

如果字符串是不可变的,那是否表示...。(假设使用JavaScript)

var str = 'foo';

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

alert(str); // foo

这是否意味着在对字符串调用方法时,它将返回修改后的字符串,但不会更改初始字符串?

如果字符串是可变的,这是否意味着第二个alert()也将返回oo

Answers:


104

这意味着一旦实例化对象,就无法更改其属性。在第一个警报中,您没有更改foo。您正在创建一个新字符串。这就是为什么在第二个警报中它将显示“ foo”而不是oo的原因。

这是否意味着在对字符串调用方法时,它将返回修改后的字符串,但不会更改初始字符串?

是。创建字符串后,任何操作都无法更改。现在,这并不意味着您不能将新的字符串对象分配给str变量。您只是无法更改str引用的当前对象。

如果字符串是可变的,这是否意味着第二个alert()也将返回oo?

从技术上讲,没有,因为substring方法返回一个新字符串。使对象可变,不会改变方法。使其可变意味着从技术上讲,您可以使它成为子字符串,而不是创建新字符串,而是更改原始字符串。


如果修改并分配了相同的字符串,将更新字符串var str ='foo'; str = str.substr(1)); // oo alert(str); // oo
Ashwin G

下面的代码呢?现在,字符串值已更改。var name =“ Santosh”; console.log(name.substr(0,2)); var name =“ kumar” console.log(name); 它如何仍然不可变
Santosh

97

在较低的级别上,不变性意味着将不会修改存储字符串的内存。创建字符串后"foo",将分配一些内存来存储值"foo"。此内存不会被更改。例如,如果您substr(1)使用新的字符串修改字符串,则将分配存储的另一部分内存"oo"。现在您的内存中有两个字符串,"foo""oo"。即使您"foo"不再使用它,它也会一直存在直到被垃圾回收。

字符串操作相对昂贵的原因之一。


1
数字也是不可变的。
RN库什瓦哈2015年

3
你好,从2015年开始!“在较低的层次上,不变性意味着不会修改存储在字符串中的内存。” ---限制太严格,听起来不正确。不变性仅与用户有关,只要保留语言规范不变式,就可以像更改内存分配器一样更改虚拟内存。
zerkms 2015年

2
这个答案比认可的答案更具逻辑性。
Ejaz Karim

但是我可以像var str ='foo'; 我可以通过str ='bar'进行修改。str值以这种方式修改了吗?
黑客

@Hacker Nope。您已为变量分配了一个新字符串,以指向内存中的新位置。
deceze

13

不可变是指无法更改或修改的内容。

因此,当您为字符串分配一个值时,该值是从头开始创建的,而不是被替换的。因此,每次将新值分配给相同的字符串时,都会创建一个副本。因此,实际上,您永远都不会改变原始值。


9

我不确定JavaScript,但是在Java中,字符串通过“字符串常量池”向不可变性迈出了又一步。可以使用字符串文字("foo")或String类构造函数来构造字符串。用字符串文字构造的字符串是字符串常量池的一部分,并且相同的字符串文字将始终是该池中的相同内存地址。

例:

    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

在上面,两者lit1lit2都是使用相同的字符串文字构造的,因此它们都指向相同的内存地址;lit1 == lit2导致true,因为它们是完全相同的对象。

但是,cons是使用类构造函数构造的。尽管参数是相同的字符串常量,但构造函数会为分配新的内存cons,这意味着conslit1和的对象不同lit2,尽管包含相同的数据。

当然,由于三个字符串都包含相同的字符数据,因此使用equals方法将返回true。

(当然,两种类型的字符串构造都是不可变的)


3

不可变意味着该值不能更改。一旦创建了字符串对象,就不能将其修改为不可变的。如果您请求字符串的子字符串,则会创建一个带有所请求部分的新字符串。

在处理字符串时使用StringBuffer可以使操作更有效,因为StringBuffer将字符串存储在具有变量的字符数组中,以保存字符数组的容量和数组的长度(char数组形式的String)


3

教科书中关于可变性的定义是有责任的,也可能会发生变化。在编程中,我们用这个词来表示其状态随时间变化的对象。一个不可变的值正好相反-创建后,就永远不会改变。

如果这看起来很奇怪,请允许我提醒您,我们一直使用的许多价值观实际上都是不可变的。

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

我认为没有人会惊讶地发现第二行绝不会改变语句中的字符串。实际上,没有字符串方法会更改它们所操作的字符串,它们都将返回新的字符串。原因是字符串是不可变的–它们不能更改,我们只能制造新的字符串。

字符串不是JavaScript内置的唯一不变值。数字也是不可变的。您甚至可以想象一个环境,其中计算表达式2 + 3会改变数字2的含义吗?听起来很荒谬,但是我们一直都在使用对象和数组来做到这一点。


2

从字符串到堆栈...摘自Eric Lippert的博客的简单易懂的示例:

在C#3.0中使用A *进行路径查找,第二部分 ...

像System.Collections.Generic.Stack这样的可变堆栈显然不适合。我们希望能够采用现有路径并为其最后一个元素的所有邻居从中创建新路径,但是将新节点压入标准堆栈会修改堆栈。我们必须先推入堆栈的副本,​​然后再推入堆栈,这很愚蠢,因为那样一来,我们将不必要地复制其所有内容。

不可变堆栈没有此问题。推入一个不变的堆栈只会创建一个全新的堆栈,该堆栈链接到旧堆栈的尾巴。由于堆栈是不可变的,因此不会有其他代码出现并弄乱尾部内容的危险。您可以继续使用旧堆栈来满足您的需求。

要深入了解不可变性,请阅读Eric从此开始的帖子:

C#中的不变性第一部分:不变性的种类


该引用包含一个奇怪的说法-堆栈数据结构(实际上是多个具有尾部共享的堆栈)总体上是可变的结构-每次推入或弹出都会改变该结构。只有单个项目是不可变的。原则上,您可以具有相同的结构,但具有可变元素。这种情况发生在一些无法确定的联合发现的IIRC中。不变性具有很大的优势,但是由于可变项的优化,完全有可能共享部分或全部数据结构。即使您希望其他引用具有明显的不变性,也可以始终根据需要进行写时复制。
2010年

0

掌握此概念的一种方法是查看javascript如何处理所有对象,这是通过引用来实现的。意味着所有对象在实例化后都是可变的,这意味着您可以添加具有新方法和属性的对象。这很重要,因为如果您希望对象是不可变的,则实例化后该对象不能更改。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.