【问题标题】:In Java, when should I prefer String over StringBuilder vice versa?在 Java 中,我什么时候应该更喜欢 String 而不是 StringBuilder,反之亦然?
【发布时间】:2015-09-10 09:28:57
【问题描述】:

我知道 StringBuilder 应该优先于 String,因为 String 将保存在常量字符串池中,并且为其分配新值不会覆盖以前的值。但是,StringBuilder 是一个覆盖其先前值的对象。

  • 在什么情况下我应该使用 String 而不是 StringBuilder,反之亦然。
  • 另外,如果我在 Hibernate 的支持类中有 String 类型的变量,我应该使用 StringBuilder 吗?怎么样?

    ...
    Result result = (Result) criteria.list().get(0);
    
    
    class Result{
       String name;
       String fname;
       ...
    }
    

【问题讨论】:

  • 只有少数字符串(文字字符串、字符串值常量表达式和您调用intern() on 的字符串)将保留在池中。为什么你暗示这是不好的? “覆盖以前的值”是什么意思?分配给String 变量与分配给StringBuilder 变量的作用相同。您是在谈论可变性与不变性吗?你确定什么时候“更喜欢”StringBuilder
  • @xehpuk 据我所知,当您为 String 分配一个新值时,它会删除对其先前值的引用并添加新值的引用,因此先前的值会留在内存中等待 GC。而 StringBuilder 将新值替换为前一个值。我不确定,这就是为什么我在第一个要点中包含(vice vesa)

标签: java hibernate jpa orm stringbuilder


【解决方案1】:

您应该使用String,因为String objects are cached in an object pool 在您不更改它们时可能会提供更好的性能。

StringBuilder 仅在您继续连接 String 标记时才有用,这在规范化的数据库表中不应该是这种情况。

JVM 会进行各种优化,即使您使用串联,JVM 也可能将该例程重写为 StringBuilder 版本。

【讨论】:

  • 另请注意,在许多情况下,可以而且应该避免串联,而无需通过中介StringBuilder。案例 1 - “jdbc” - 使用绑定变量与连接字符串;案例 2 - “System.out” - 多次调用 printprintln,或链接 System.out,如 System.out.append("Hello ").append(name);
【解决方案2】:

一个简单的经验法则(String 是一种表示字符串的类型。StringBuilder 是可变字符流)

使用String 表示文本值。根据定义,Java 提供了字符串值的池化,从而为您提供了一些空间优化。在您的应用程序在文件批处理期间处理数百万个文本值的场景中考虑这一点。举个例子。

  String str1 = "Test";
  String str2 = "Test";

这里,str1 == str2(相同的引用)

另外,+ 运算符在 String 中被重载以从不同类型构造 String。这可以在构造小字符串时使用(在内部使用StringBuilder 完成,所以不用担心) - 但不是在循环时。

仅当您使用不同类型的小片段构造目标字符串时使用StringBuilder(或旧的对应物StringBuffer) - 并且尤其是在循环内 - 这将帮助您避免放置字符串池中不必要的字符串片段。

   StringBuilder s1 = new StringBuilder("test");
   StringBuilder s2 = new StringBuilder("test");

这里,s1 != s2

另外,我认为您无法通过某种方式操纵 StringBuilder/Buffer 的编码 - 同时 String 允许这样做。

编辑: 表示 Hibernate 实体: 始终使用String 来表示您班级中的文本类型。由于上述原因。这应该像肌肉记忆一样带给你。例如,intfloatchar 等用于原始类型,String 用于文本类型。仅使用构建器来构建字符串,而不是表示类型,除非这是一些奇怪的要求。

【讨论】:

【解决方案3】:

对于固定的文本值,我会使用 String。

在创建较大的文本字符串时使用 StringBuilder,例如:

final StringBuilder sb = new StringBuilder();
for(;;){
    sb.append("more text\n");//add text, StringBuilder's size will increase incrementally
    if(this.stop()){
         break;//stop loop
    }
}
final String result = sb.toString();// Create the final result to work with
System.out.println(result);//print result

对同步值使用 StringBuffer 而不是 StringBuilder, 有关 StringBuilder 和 StringBuffer 的区别,请参见 https://stackoverflow.com/a/355092/928952

JavaDoc:StringBuffer (http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuffer.html):

线程安全、可变的字符序列。字符串缓冲区就像 一个字符串,但可以修改。在任何时候它都包含一些 特定的字符序列,但长度和内容 顺序可以通过某些方法调用来改变

JavaDoc:StringBuilder (http://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html):

可变的字符序列。这个类提供了一个 API 与 StringBuffer 兼容,但不保证 同步。此类旨在用作插入式 在字符串缓冲区所在的地方替换 StringBuffer 由单个线程使用(通常是这种情况)。在哪里 可能,建议优先使用此类 StringBuffer,因为它在大多数实现下会更快。

JavaDoc:字符串(http://docs.oracle.com/javase/7/docs/api/java/lang/String.html):

String 类表示字符串。中的所有字符串文字 Java 程序,例如“abc”,被实现为这个的实例 班级。字符串是常量;他们的价值观在他们之后不能改变 被创建。字符串缓冲区支持可变字符串。因为字符串 对象是不可变的,它们可以共享

基本上,您将使用字符串作为文本的常量(不可变)。

【讨论】:

  • 那么这是否意味着我的休眠代码是正确的?
  • 我没有看到错误...但这并不意味着错误不在其他地方。
  • 我没有投反对票,但我会避免使用 StringBuilder。使事情线程安全需要仔细考虑实现,而不是盲目地转向所有 API 的线程安全实现。在很多情况下,即使在多线程上下文中,也并不总是需要保护代码的所有部分。像 StringBuilder 这样的线程安全实现,以及一些 Collection 实现会增加很多同步开销。
  • 我不会在不同的线程上使用 String 构建,听起来太容易出错。我也想不出有必要这样做的原因 - 但也许其他人会
【解决方案4】:

当您必须对字符串进行大量修改时,应该使用 java.lang.StringBuilder 类。我们知道 String 对象是不可变的,因此如果您选择对 String 对象进行大量操作,您最终会在字符串池中产生大量废弃的字符串对象。 (即使在现在有千兆字节的 RAM 的时代,将宝贵的内存浪费在丢弃的 String 池对象上也不是一个好主意。)另一方面,StringBuilder 类型的对象可以一遍又一遍地修改,而不会留下大量丢弃的影响。字符串对象。

String x = "abc";
x.concat("def");
System.out.println("x = " + x); // output is "x = abc"


StringBuffer sb = new StringBuffer("abc");
sb.append("def");
System.out.println("sb = " + sb); // output is "sb = abcdef"

【讨论】:

  • 字符串操作不会将对象留在“字符串池”中。有一个唯一字符串池,但只有当您 intern() 或者它们来自源代码中的字符串文字时,字符串才会结束。并首先打印StringBuffer将其转换为String,所以这无济于事。
【解决方案5】:

String 是不可变对象,一旦创建就无法更改。作为字符串创建的对象存储在常量字符串池中。 Java 中的每个不可变对象都是线程安全的,这意味着 String 也是线程安全的。 字符串不能被两个线程同时使用。字符串一旦分配就不能更改。

String  demo = "Test1" ;

上述对象存储在常量字符串池中,其值不可修改。

demo="Test2" ;

在常量池中创建新的“Test2”字符串并由演示变量引用

StringBuilder 对象是可变的,我们可以对对象中存储的值进行更改。这实际上意味着如果使用 StringBuilder 对象而不是 String 对象来执行诸如追加之类的字符串操作,则效率会更高。

StringBuilder demo2= new StringBuilder("Test1");

上述对象也存储在堆中,其值可以修改

demo2=new StringBuilder("Test1"); 

上面的语句是正确的,因为它修改了StringBuilder中允许的值。

因此,您应该在需要频繁更新/修改字符串的地方使用 stringBuilder。

【讨论】:

  • “创建为字符串的对象”默认不存储在“常量字符串池”中。只有当您 intern() 或者它们来自源代码中的字符串文字时,字符串才会结束。 new StringBuilder("Test1") 还将字符串 "Test1" 存储在“常量字符串池”中,因为 "Test1" 是一个字符串,因此在这方面使用 StringBuilder/Buffer 并没有什么好处。
  • 是的,如果字符串是用 new 初始化的,那么实习生会进入图片,然后它会移动到池中
  • No Abhijit,使用new 构造的字符串不会移动到池中。只有调用 intern() 或在你的类中有一个字符串文字才能做到这一点。
  • 抱歉简短的回复。是的,我同意你的说法。这就是我在这里试图提到的是,当一个字符串用 new 初始化,然后如果你调用实习生,它将移动到池中。 Intern 是将字符串添加到池中的方法,如果它们存在于池中...
猜你喜欢
  • 1970-01-01
  • 2017-10-21
  • 2010-11-14
  • 2011-12-07
  • 2022-01-08
  • 2011-05-10
  • 2010-10-23
  • 2010-12-03
  • 2017-12-21
相关资源
最近更新 更多