【问题标题】:About string concatenation behaviours关于字符串连接行为
【发布时间】:2015-08-15 01:02:04
【问题描述】:

我明白,考虑到字符串的不变性,像

String a="";
for(int i=0;i++<9;)
    a+=i;

效率非常低,因为最初实例化一个字符串并将其放入 字符串池,然后使用a+=i 创建一个新字符串(在第一个循环中 0 ),由a 引用,前一个现在可以进行垃圾收集。这种情况发生了九次。

更好的方法是使用StringBuilder:

StringBuilder a=new StringBuilder("");
for(int i=0;i++<9;)
    a.append(i);

但是当我用 new关键字?

String a=new String("");
for(int i=0;i++<9;)
    a+=i;

我知道在这种情况下a 不会被实习(它不在字符串池中),但它仍然是不可变的吗?这个时候a+=i指令做了什么?行为是否与我的第一个示例相同?

【问题讨论】:

  • 是什么让你认为在你的第一个版本中所有的字符串都放在了池中?只有空字符串会在字符串池中......其余的只会在堆上。是的,所有字符串都是不可变的,无论它们是否来自常量池。
  • @JonSkeet 感谢您的澄清......我有点困惑

标签: java string stringbuilder string-pool


【解决方案1】:

只有 String literalsStrings 你调用了 intern() 方法被放入字符串池中。连接不会自动生成 String,因此您的示例与字符串池相同。

String abc = new String("abc"); //"abc" is put on the pool
abc += "def"; //"def" is put on the pool, but "abcdef" is not
String xyz = "abcdefghi".substring(0, 6).intern(); //"abcdef" is now added to the pool and returned by the intern() function
String xyz = "test"; //test is put on the pool
xyz += "ing"; //ing is put on the pool, but "testing" is not

要对此进行扩展,请注意 String 构造函数不会自动实习(或不是实习)字符串。使用 String literals(代码中的引号中的字符串)是导致字符串位于字符串池中的原因。

String abc = "abc"; //"abc" is in the pool
String def = "def"; //"def" is in the pool
String str1 = new String(abc + def); //"abcdef" is not in the pool yet
String str2 = new String("abcdef"); //"abcdef" is on the pool now

还要注意String 复制构造函数几乎从不使用,因为字符串无论如何都是不可变的。

如需了解更多信息,请阅读答案hereherehere

【讨论】:

  • 清除!我认为实习字符串的过程和它的不变性之间存在联系
  • String abc = new String("abc"); 去游泳池??我的书不同意,或者我真的很困惑......
  • 字符串“abc”是一个字符串literal,所以是的,它在池中。构造函数不会自动实习(或 not 实习)字符串。
  • 那么,您的意思是文字 "abc" 进入池,但变量 abc 没有?
  • @LuigiCortese 我不确定你在问什么。变量本身永远不会被实习,所以这有点不成问题。我已经编辑了答案以包含有关构造函数的信息。
【解决方案2】:

让我们考虑你的第一个例子:

String a="";
for(int i=0;i++<9;)
    a+=i;

这段代码会这样执行:

String a="";
a=new StringBuilder(a).append(0).toString();
a=new StringBuilder(a).append(1).toString();
a=new StringBuilder(a).append(2).toString();
a=new StringBuilder(a).append(3).toString();
a=new StringBuilder(a).append(4).toString();
a=new StringBuilder(a).append(5).toString();
a=new StringBuilder(a).append(6).toString();
a=new StringBuilder(a).append(7).toString();
a=new StringBuilder(a).append(8).toString();
a=new StringBuilder(a).append(9).toString();

因此,对于每个循环迭代,您将从字符串创建一个新的StringBuilder(每次分配一个新的内部char[] 缓冲区),每次都将转换回String。另一方面,在第二种情况下,您将有效地拥有

StringBuilder a=new StringBuilder("").append(0).append(1).append(2).append(3)
    .append(4).append(5).append(6).append(7).append(8).append(9);

因此,您将只有一个 StringBuilder 和一个内部 char[] 缓冲区(在您的情况下,它不会重新分配,因为所有附件都不会超过 16 的初始容量)。所以它只是更快,因为你有更少的对象并且没有被多次复制。

a=new String("") 是没有用的,因为除了已经拥有的空字符串(在加载类常量池期间创建和实习)之外,您还会有一个空字符串。除了额外的空字符串(在第一次循环迭代和垃圾收集后将变为未使用)之外,它与第一种情况没有什么不同。

请注意,在 JLS 中并没有具体说明如何实现字符串连接(使用隐式 StringBuilder 或其他一些技术),但通常 java 编译器使用 StringBuilder 对其进行翻译。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2016-11-22
    相关资源
    最近更新 更多