我一直想知道为什么JAVA和C#有String
(不可变和线程安全)类,如果他们有StringBuilder
(可变和非线程安全)或StringBuffer
(mutable& threadsafe)类。不是StringBuilder
类的StringBuffer
/ String
超集吗?我的意思是,如果我选择使用String
/ StringBuilder
,我为什么要使用StringBuffer
课程?
例如,而不是使用以下,
String str;
为什么我总是不能使用以下内容?
StringBuilder strb; //or
StringBuffer strbu;
简而言之,我的问题是,如果我用String
类替换StringBuffer
,我的代码将如何生效?此外,StringBuffer
增加了可变性的优势。
答案 0 :(得分:16)
我的意思是,如果我可以选择使用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.
不变性使分享变得简单而有效。这对多线程代码特别有用。它使得“修改”(即创建具有不同数据的新实例)更加痛苦,但在许多情况下这绝对没问题,因为值在没有被修改的情况下通过系统。
不可变性特别对“简单”类型有用,例如字符串,日期,数字(BigDecimal
,BigInteger
等)。它允许它们更容易在地图中使用,它允许简单的等式定义等。
答案 1 :(得分:3)
1)StringBuilder
以及StringBuffer
都是mutable
。因此,它会导致一些问题,例如在hashMap
中使用像集合这样的集合。请参阅this link。
不可变性优势的另一个例子是Jon
在评论中提到的。我只是在这里粘贴。
有人可以使用最初通过我的验证标准的构建器调用
Person p = new Person(builder);
- 然后在之后修改它,而Person类中没有任何说法。为了避免这种情况,Person类需要复制经过验证的数据。
Immutabilty确保不会发生这种情况。
2)由于string
是java中使用最广泛的对象,string pool
提供重用相同的字符串,从而节省内存。
答案 2 :(得分:0)
我完全同意Jon Skeet不可变性是使用preg_match_all('~(?:ab|cr)\d+~', $str, $match);
的一个原因。另一个原因(从C#角度来看)String
实际上比String
重量轻。如果您查看String和String Builder的参考来源,您会看到StringBuilder
实际上有一些StringBuilder
个常量。作为开发人员,您应该只使用您需要的内容,除非您需要String
提供的附加权益,否则您应该使用StringBuilder
。
答案 3 :(得分:0)
许多答案已经概述了使用StringBuilder
等可变变体的缺点。为了说明问题,使用StringBuilder
无法实现的一件事是关联内存,即哈希表。当然,大多数实现都允许您使用StringBuilder
作为哈希表的键,但它们只能找到完全相同的StringBuilder
实例的值。但是,您希望实现的典型行为是,字符串来自何处并不重要,因为只有字符很重要,例如,从数据库或文件(或任何其他外部资源)中重新生成字符串。
但是,据我了解你的问题,你主要是询问字段类型。事实上,我认为你的观点特别考虑到我们对其他对象的集合做了完全相同的事情,这些对象通常不是不可变对象,而是可变集合,例如C#中的List
或ArrayList
或者Java,分别。最后,一个字符串只是一个字符集合,为什么不让它变成可变的呢?
我在这里给出的答案是,这种字符串如何改变的通常行为与通常的集合非常不同。如果你有一个后续元素的集合,那么通常只向集合中添加一个元素,使大部分集合保持不变,即你不会丢弃一个列表来插入一个项目,至少除非你在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
版本在我的机器上完成1750毫秒,而StringBuilder
花费了2245毫秒。但是,这两个版本都相当快,在这种情况下性能影响可以忽略不计。
答案 4 :(得分:0)
我想在String
和StringBuilder
类之间添加一些差异:
是的,如上所述String
是不可变的类,并且在创建字符串后无法更改内容。允许在不锁定的情况下使用来自不同线程的相同字符串对象。
如果需要将大量字符串连接在一起,请使用StringBuilder
类。当您使用“+”运算符时,它会在托管堆上创建大量字符串对象并损害性能。
StringBuilder
是可变类。 StringBuilder
将字符存储在数组中,并且可以使用字符进行操作而无需创建新的字符串对象(例如add,remove,replace,append)。
如果您知道结果字符串的大致长度,则应设置容量。默认容量为16(.NET 4.5)。它为您提供了性能改进,因为StringBuilder
具有内部字符数组。当字符数超过当前容量时,字符数组会重新创建。
答案 5 :(得分:-1)
<强> String
强>:
出于性能和内存消耗的目的,使用StringBuilder
是有意义的。