为什么我们有String类,如果StringBuilder或StringBuffer可以做String的作用?
我一直想知道为什么JAVA和C#有String
(immutable&threadsafe)类,如果它们有StringBuilder
(mutable&not threadsafe)或StringBuffer
(mutable&threadsafe)类。 不是String
类的StringBuilder
/ StringBuffer
超集吗? 我的意思是,如果我可以使用StringBuilder
/ StringBuffer
,我为什么要使用String
类?
例如,而不是使用以下,
String str;
为什么我不能一直使用以下?
StringBuilder strb; //or StringBuffer strbu;
简而言之,我的问题是,如果我用StringBuffer
类替换String
,我的代码将如何受到影响? 此外, StringBuffer
还增加了可变性的优势。
我的意思是,如果我可以使用StringBuilder / StringBuffer,我为什么要使用String类?
正是因为它是不可改变的。 不变性有很多好处,主要是它可以更容易地推理你的代码,而无需在任何地方创建数据副本“以防万一”某些东西决定改变价值。 例如:
private readonly String name; public Person(string name) { if (string.IsNullOrEmpty(name)) // Or whatever { // Throw some exception } this.name = name; } // All the rest of the code can rely on name being a non-null // reference to a non-empty string. Nothing can mutate it, leaving // evil reflection aside.
不变性使分享变得简单而有效。 这对multithreading代码特别有用。 它使“修改”(即创建具有不同数据的新实例)更加痛苦,但在许多情况下这绝对没问题,因为值在没有被修改的情况下通过系统。
不可变性对于“简单”类型特别有用,例如字符串,日期,数字( BigDecimal
, BigInteger
等)。 它允许它们更容易在地图中使用,它允许简单的相等定义等。
1) StringBuilder
和StringBuffer
都是mutable
。 因此,它会导致一些问题,例如在hashMap
使用像集合中的键hashMap
。 看到这个链接 。
另一个不可变性优势的例子将是Jon
在他的评论中提到的。 我只是在这里粘贴。
有人可以调用
Person p = new Person(builder);
使用最初通过我的validation标准的构建器 – 然后在之后修改它,而Person类没有任何发言权。 为了避免这种情况,Person类需要复制经过validation的数据。
不可避免性确保不会发生这种情况。
2)由于string
是java中最广泛使用的对象, string pool
提供重用相同的字符串,从而节省内存。
我完全同意Jon Skeet的说法,不变性是使用String
一个原因。 另一个原因(从C#角度来看)是String
实际上比StringBuilder
更轻。 如果你查看String和String Builder的引用源,你会发现StringBuilder
实际上有许多String
常量。 作为开发人员,您应该只使用您需要的内容,除非您需要StringBuilder
提供的额外好处,否则您应该使用String
。
许多答案已经概述了使用可变变体(如StringBuilder
存在缺点。 为了说明这个问题,使用StringBuilder
无法实现的一件事是关联内存,即哈希表。 当然,大多数实现都允许您使用StringBuilder
作为哈希表的键,但它们只能找到完全相同的StringBuilder
实例的值。 但是,您希望实现的典型行为是,字符串来自哪里并不重要,因为只有字符很重要,例如从数据库或文件(或任何其他外部资源)重新生成字符串。
但是,据我了解你的问题,你主要是询问字段类型。 事实上,我认为你的观点特别考虑到我们对其他对象的集合做了完全相同的事情,这些对象通常不是不可变对象,而是可变集合,例如C#或Java中的List
或ArrayList
。 最后,字符串只是一个字符集合,为什么不让它变成可变的?
我在这里给出的答案是,这种字符串如何改变的通常行为与通常的集合非常不同。 如果你有一个后续元素的集合,通常只向集合中添加一个元素,使大部分集合保持不变,即你不会丢弃列表来插入一个项目,至少除非你在Haskell中编程:)。 对于许多字符串,如名称,这是不同的,因为您通常替换整个字符串。 鉴于字符串数据类型的重要性,平台通常为诸如实习字符串之类的字符串提供大量优化,使得选择更偏向于字符串。
但是,最后,每个程序都是不同的,你可能有一些要求,默认情况下使用StringBuilder
更合理,但由于给定的原因,我认为这些情况相当罕见。
编辑:正如你要求的例子。 请考虑以下代码:
stopwatch.Start(); var s = ""; for (int i = 0; i < 100000; i++) { s = "." + s; } stopwatch.Stop(); Console.WriteLine(stopwatch.ElapsedMilliseconds); stopwatch.Restart(); var s2 = new StringBuilder(); for (int i = 0; i < 100000; i++) { s2.Insert(0, "."); } stopwatch.Stop(); Console.WriteLine(stopwatch.ElapsedMilliseconds);
从技术上讲,两个位都做了非常类似的事情,他们会在第一个位置插入一个字符,并移动后面的任何内容。 这两个版本都涉及复制以前在那里的整个字符串。 带有string
的版本在我的机器上完成1750ms,而StringBuilder
需要2245ms。 但是,这两个版本都相当快,在这种情况下性能影响可以忽略不计。
我想在String
和StringBuilder
类之间添加一些区别:
是的,如上所述String
是不可变的类,并且在创建字符串后无法更改内容。 允许在不锁定的情况下使用来自不同线程的相同字符串对象。 如果需要将许多字符串连接在一起,请使用StringBuilder
类。 当您使用“+”运算符时,它会在托管堆上创建大量字符串对象并损害性能。
StringBuilder
是可变类。 StringBuilder
将字符存储在数组中,并且可以使用字符进行操作,而无需创建新的字符串对象(例如add,remove,replace,append)。 如果您知道结果字符串的大致长度,则应设置容量。 默认容量为16(.NET 4.5)。 它为您提供性能改进,因为StringBuilder
具有内部字符数组。 当字符数超过当前容量时,字符数组会重新创建。
String
:
- 是不可变的(所以你可以在集合中使用它)
- 每个操作都在堆上创建一个新实例。 从技术上讲,这取决于代码。
出于性能和内存消耗的目的,使用StringBuilder
是有意义的。