如果字符串是不可变的,那是否意味着.... (让我们假设JavaScript)
var str = 'foo';
alert(str.substr(1)); // oo
alert(str); // foo
这是否意味着,当在字符串上调用方法时,它将返回修改后的字符串,但它不会更改初始字符串?
如果字符串是可变的,这是否意味着第二个alert()
也会返回oo
?
答案 0 :(得分:103)
这意味着一旦实例化对象,就无法更改其属性。在你的第一个警报中,你不会改变foo。你正在创建一个新的字符串。这就是为什么在你的第二个警报中它会显示“foo”而不是oo。
在调用方法时是否意味着 一个字符串,它将返回修改后的 字符串,但它不会改变 初始字符串?
是。创建字符串后,没有任何内容可以更改。现在,这并不意味着您无法将新的字符串对象分配给str
变量。您无法更改str引用的当前对象。
如果字符串是可变的,那就是 意味着第二个警报()将返回oo 还有吗?
从技术上讲,不,因为substring方法返回一个新字符串。使对象变为可变,不会改变方法。使其变为可变意味着从技术上讲,您可以使子串会改变原始字符串而不是创建新字符串。
答案 1 :(得分:93)
在较低级别,不变性意味着不会修改存储字符串的内存。创建字符串"foo"
后,会分配一些内存来存储值"foo"
。这个记忆不会改变。如果使用substr(1)
修改字符串,则会创建一个新字符串,并分配一个将存储"oo"
的内存的不同部分。现在内存中有两个字符串,"foo"
和"oo"
。即使你不再使用"foo"
,它也会一直存在,直到收集垃圾为止。
字符串操作相对昂贵的一个原因。
答案 2 :(得分:13)
不可变意味着无法更改或修改。
因此,当您为字符串赋值时,此值是从头开始创建的,而不是被替换。因此,每次为同一字符串分配新值时,都会创建一个副本。所以实际上,你永远不会改变原来的价值。
答案 3 :(得分: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
在上文中,lit1
和lit2
都是使用相同的字符串文字构造的,因此它们指向相同的内存地址; lit1 == lit2
会产生true
,因为它们是完全相同的对象。
但是,cons
是使用类构造函数构造的。虽然参数是相同的字符串常量,但构造函数为cons
分配了新内存,这意味着cons
与lit1
和lit2
不是同一个对象,尽管包含相同的数据
当然,由于这三个字符串都包含相同的字符数据,因此使用equals
方法将返回true。
(两种类型的字符串结构当然都是不可变的)
答案 4 :(得分:3)
不可变意味着价值无法改变。一旦创建了一个字符串对象就不能被修改为其不可变的。如果您请求字符串的子字符串,则会创建一个包含所请求部分的新String。
在操作字符串时使用StringBuffer会使操作更有效率,因为StringBuffer将字符串存储在带有变量的字符数组中,以保存字符数组的容量和数组的长度(以字符数组形式表示的字符串)
答案 5 :(得分:3)
可变性的教科书定义有责任或可能会有变更或更改。 在编程中,我们使用这个词来表示允许状态随时间变化的对象。不可变的值恰恰相反 - 在创建之后,它永远不会改变。
如果这看起来很奇怪,请允许我提醒您,我们一直使用的许多值实际上都是不可变的。
var statement = "I am an immutable value";
var otherStr = statement.slice(8, 17);
我认为没有人会惊讶地发现第二行绝不会改变语句中的字符串。 实际上,没有字符串方法可以更改它们操作的字符串,它们都返回新的字符串。原因是字符串是不可变的 - 它们不能改变,我们只能创建新的字符串。
字符串不是JavaScript中内置的唯一不可变值。数字也是不可改变的。你能想象一个评估表达式2 + 3会改变数字2意义的环境吗?这听起来很荒谬,但我们一直用我们的对象和数组来做这件事。
答案 6 :(得分:2)
从字符串到堆栈...从Eric Lippert's blog获取的简单易懂的示例:
Path Finding Using A* in C# 3.0, Part Two ...
像System.Collections.Generic.Stack这样的可变堆栈 显然不适合。我们想成为 能够采取现有的路径和 为它创造新的路径 最后一个元素的邻居, 但推动一个新节点 标准堆栈修改堆栈。 我们必须复制堆栈 在推它之前,这是愚蠢的 因为那时我们会重复所有 其内容不必要。
不可变堆栈没有此问题。推动不变的 堆栈只是创建一个全新的堆栈 链接到旧的 尾巴。由于堆栈是不可变的, 没有其他代码的危险 来了,弄乱了尾巴 内容。你可以继续使用 老堆栈到你心里的内容。
要深入了解不可变性,请阅读Eric的帖子,从这一篇开始:
答案 7 :(得分:0)
掌握这个概念的一种方法是查看javascript如何处理所有对象,这是通过引用。这意味着在实例化之后所有对象都是可变,这意味着您可以添加具有新方法和属性的对象。这很重要,因为如果您希望对象是不可变的,则对象在实例化后不能更改。
答案 8 :(得分:0)
尝试:
let string = "name";
string[0] = "N";
console.log(string); // name not Name
string = "Name";
console.log(string); // Name
因此,这意味着字符串是不可变的,但不是恒定的,用简单的话来说,可以进行重新分配,但不能更改某些部分。