字符串不变性是通过语句还是通过语句中的字符串来工作的?
例如,我了解以下代码将在堆上分配两个字符串。
string s = "hello ";
s += "world!";
“你好”将留在堆上,直到垃圾收集;现在引用“你好世界!”在堆上。但是,以下行在堆上分配了多少个字符串... 1或2?此外,是否有工具/方法来验证结果?
string s = "goodbye " + "cruel world!";
答案 0 :(得分:21)
编译器对字符串连接有特殊处理,这就是为什么第二个例子只是一个字符串。而“实习”意味着即使你运行这条线20000次,仍然只有1个字符串。
重新测试结果......最简单的方法(在这种情况下)可能是看反射器:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] string s)
L_0000: ldstr "goodbye cruel world!"
L_0005: stloc.0
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(string)
L_000c: ret
}
正如您所见(ldstr
),编译器已经为您完成了这个。
答案 1 :(得分:3)
字面值字符串为interned这意味着"hello "
不驻留在堆上但位于数据段 [请参阅注释] 中progam(因此不符合垃圾收集条件),"world"
也是如此,如果编译器足够智能的话,"hello world"
也可能被实习。
"goodbye cruel world"
将被实习,因为字符串文字连接是编译器处理的事情。
修改:我不确定数据段声明,有关详细信息,请参阅this question。
答案 2 :(得分:0)
实际上,可能是3.“forbye”的常量字符串,“残忍世界”的常量字符串,然后是结果的新字符串。
您可以通过查看生成的代码来确定。这取决于编译器,(事实上,在语言上,这并不明显)但你可以通过使用-a标志来读取g ++的输出(我想,检查手册页)以获取中间代码
答案 3 :(得分:0)
不要相信你对字符串的“了解”。您可以查看源代码以了解string的实现。比如你的例子:
string s = "goodbye " + "cruel world!";
在java中会分配一个字符串。 Java扮演一些非常可爱的技巧,并且难以智取 - 只是在你需要之前永远不会优化!
目前,据我所知,使用此:
String s="";
for(int i=0;i<1000;i++)
s+=" ";
创建1000个空格字符串仍然效率极低
在循环中追加非常糟糕,但除此之外它可能与StringBuilder一样高效。
答案 4 :(得分:0)
这里要小心,因为编译器可以在编译时知道字符串值时进行一些非常不同的优化。如果您使用的字符串直到运行时才知道(从配置文件,数据库或用户输入中提取),您将看到一些非常不同的IL。
答案 5 :(得分:0)
如果你打算做一两个字符串连接,我就不用担心了。
但是如果你有很多连接,或者你有一个循环,那么你肯定想采取预防措施。在Java世界中,这意味着您使用StringBuffer而不是连接字符串。
答案 6 :(得分:0)
如果它不只是在一行中,可以通过将第一个字符串放入StringBuffer,进行连接并返回结果字符串来实现两个字符串的串联。
自己创建StringBuffer可能看起来有些过分,但无论如何都会发生这种情况.-
答案 7 :(得分:0)
无论如何都不要过早地优化,但不要打折高性能字符串连接的性能。它不是对象创建,而是它导致的GC工作。
有一个实验室(ASP.NET升级工程师)Tess Ferrnandez's博客显示how string concatonation can bring a server to its knees的一个(相当极端的,授予的)示例。
答案 8 :(得分:-1)
如果编译器是“智能”的,它只会是一个带有“再见残酷世界!”的字符串。