String,StringBuffer和StringBuilder
请告诉我一个实时的情况来比较String
, StringBuffer
和StringBuilder
?
可变性差异:
String
是不可变的 ,如果你试图改变它们的值,另一个对象被创build,而StringBuffer
和StringBuilder
是可变的,所以他们可以改变它们的值。
线程安全差异:
StringBuffer
和StringBuilder
的区别在于StringBuffer
是线程安全的。 所以当应用程序只需要在一个线程中运行时,最好使用StringBuilder
。 StringBuilder
比StringBuffer
更高效。
情况:
- 如果你的string不会改变,使用一个String类,因为一个
String
对象是不可变的。 - 如果你的string可以改变(例如:string构造中的大量逻辑和操作)并且只能从单个线程访问,那么使用
StringBuilder
就足够了。 - 如果您的string可以更改,并且将从多个线程访问,请使用
StringBuffer
因为StringBuffer
是同步的,所以您具有线程安全性。
- 当一个不可变的结构是适当的时候你使用
String
; 从一个String
获取一个新的字符序列可能会在CPU时间或内存中获得不可接受的性能损失(因为数据不被复制而获得子系统的CPU效率,但这意味着可能保留更多数量的数据)。 - 当你需要创build一个可变的字符序列时,你可以使用
StringBuilder
,通常是将多个字符序列连接在一起。 - 在使用
StringBuffer
的情况下使用StringBuilder
,但是当对底层string的改变必须同步时(因为有多个线程正在读/修改string缓冲区)。
在这里看到一个例子。
基础:
String
是一个不可变的类,它不能被改变。 StringBuilder
是一个可以附加到可变类,字符被replace或删除并最终转换为String
StringBuffer
是StringBuffer
的原始同步版本
在所有只有一个线程访问您的对象的情况下,您应该更喜欢StringBuilder
。
细节:
另外请注意, StringBuilder/Buffers
不是魔术,它们只是使用一个数组作为支持对象,并且该数组在满满时必须重新分配。 一定要创build足够大的StringBuilder/Buffer
对象,在每次调用.append()
时都不需要经常重新resize。
重新调整可能变得非常堕落。 每次需要扩展时,基本上将后端数组的大小重新调整为当前大小的2倍。 这可能会导致大量的RAM分配,并且在StringBuilder/Buffer
类开始变大时不使用。
在Java String x = "A" + "B";
在幕后使用了一个StringBuilder
。 所以对于简单的情况来说,宣布你自己没有任何好处。 但是,如果你正在build立大的String
对象,比如小于4k,那么声明StringBuilder sb = StringBuilder(4096);
比串联或使用只有16个字符的默认构造函数效率更高。 如果你的String
将小于10K,那么使用构造函数将它初始化为10k是安全的。 但是,如果它初始化为10k,那么你写了10k以上的1个字符,它将被重新分配并复制到一个20k数组。 所以初始化高于低于。
在自动重新设置大小写的情况下,在第17个字符后面的数组被重新分配并复制到32个字符,在第33个字符处会再次发生,您将重新分配并将该数组复制为64个字符。 你可以看到这是如何退化到大量的重新分配和复制,这是你真正试图避免使用StringBuilder/Buffer
的第一位。
这是来自AbstractStringBuilder的JDK 6源代码
void expandCapacity(int minimumCapacity) { int newCapacity = (value.length + 1) * 2; if (newCapacity < 0) { newCapacity = Integer.MAX_VALUE; } else if (minimumCapacity > newCapacity) { newCapacity = minimumCapacity; } value = Arrays.copyOf(value, newCapacity); }
最好的做法是初始化StringBuilder/Buffer
比你想象的要大一点,如果你不知道String
有多大,但你可以猜到。 比你需要更多的内存分配将比大量的重新分配和复制更好。
另外要小心用String
来初始化一个StringBuilder/Buffer
,因为这只会分配String + 16个字符的大小,在大多数情况下,它只会启动您试图避免的简并重新分配和复制循环。 以下是直接从Java 6源代码。
public StringBuilder(String str) { super(str.length() + 16); append(str); }
如果偶然发生了一个没有创build的StringBuilder/Buffer
的实例,并且无法控制被调用的构造函数,那么有一种方法可以避免退化的重新分配和复制行为。 调用.ensureCapacity()
以确保您的结果String
适合的大小。
替代品:
正如一个笔记,如果你正在做非常重的 String
build设和操纵,还有一个更多的性能导向的替代品称为绳索 。
另一种方法是通过对ArrayList<String>
进行子分类来创build一个StringList
实现,并添加计数器来跟踪列表中每个.append()
和其他变异操作上的字符数,然后覆盖.toString()
以创build一个您需要的确切大小的StringBuilder
并循环遍历列表并构build输出,甚至可以使该StringBuilder
成为实例variables,并“caching” .toString()
的结果,并且只有在某些更改时才能重新生成结果。
当编译固定的格式化输出的时候也不要忘记String.format()
,编译器可以使它更好的进行优化。
你的意思是连接?
真实世界的例子: 你想创build一个新的string出其他许多人 。
例如发送消息:
串
String s = "Dear " + user.name + "<br>" + " I saw your profile and got interested in you.<br>" + " I'm " + user.age + "yrs. old too"
StringBuilder的
String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" ) .append(" I saw your profile and got interested in you.<br>") .append(" I'm " ).append( user.age ).append( "yrs. old too") .toString()
要么
String s = new StringBuilder(100).appe..... etc. ... // The difference is a size of 100 will be allocated upfront as fuzzy lollipop points out.
StringBuffer(语法与StringBuilder完全一样,效果不同)
关于
StringBuffer
与StringBuilder
前者是同步的,后来不是。
所以,如果你在单个线程(这是90%的情况)中多次调用它, StringBuilder
将运行得更快,因为它不会停止查看它是否拥有线程锁。
所以,build议使用StringBuilder
(除非你有多个线程同时访问,这很less见)
String
连接( 使用+运算符 )可能会被编译器优化,以便在下面使用StringBuilder
,因此,不再需要担心,在Java的前几天,这是所有人都应该避免的代价,因为每个连接都创build一个新的String对象。 现代编译器不再这样做了,但是,如果使用“旧”编译器,使用StringBuilder
是一个好习惯。
编辑
对于好奇的人来说,这是编译器为这个类所做的:
class StringConcatenation { int x; String literal = "Value is" + x; String builder = new StringBuilder().append("Value is").append(x).toString(); }
javap -c StringConcatenation
Compiled from "StringConcatenation.java" class StringConcatenation extends java.lang.Object{ int x; java.lang.String literal; java.lang.String builder; StringConcatenation(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: aload_0 5: new #2; //class java/lang/StringBuilder 8: dup 9: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V 12: ldc #4; //String Value is 14: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17: aload_0 18: getfield #6; //Field x:I 21: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 24: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 27: putfield #9; //Field literal:Ljava/lang/String; 30: aload_0 31: new #2; //class java/lang/StringBuilder 34: dup 35: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V 38: ldc #4; //String Value is 40: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 43: aload_0 44: getfield #6; //Field x:I 47: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 50: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 53: putfield #10; //Field builder:Ljava/lang/String; 56: return }
编号为5-27的行用于名为“literal”的string
编号为31-53的行用于名为“builder”的string
Ther没有什么区别,两个string的执行完全相同。
此外, StringBuffer
是线程安全的,而StringBuilder
不是。
因此,在不同线程访问它的实时情况下, StringBuilder
可能具有不确定的结果。
请注意,如果您使用Java 5或更新版本,则应该使用StringBuilder
而不是StringBuffer
。 从API文档:
从版本JDK 5开始,这个类已经补充了一个为单个线程
StringBuilder
devise的等价类。 由于StringBuilder
类支持所有相同的操作,所以通常会优先使用StringBuilder
类,但速度更快,因为它不执行同步操作。
实际上,你几乎不会同时在多个线程中使用它,所以StringBuffer
所做的同步几乎总是不必要的开销。
String和其他两个类的区别在于String是不可变的,另外两个是可变类。
但是为什么我们有两个同样的目的?
原因是StringBuffer
是线程安全的, StringBuilder
不是。 StringBuilder
是StringBuffer Api
上的一个新类,它在JDK5
中引入,如果您在单线程环境中工作,则总是推荐使用它,因为它的Faster
要快得多
有关完整的详细信息,请阅读http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/
就个人而言,我不认为有任何真正的世界用于StringBuffer
。 我什么时候想通过操纵字符序列来在多个线程之间进行通信? 这听起来没有用处,但也许我还没有看到光:)
在Java中, string是不可变的。 作为不可变的,我们的意思是一旦创build了一个string,我们不能改变它的值。 StringBuffer是可变的。 一旦创build了一个StringBuffer对象,我们只需将内容追加到对象的值而不是创build一个新的对象。 StringBuilder类似于StringBuffer,但它不是线程安全的。 StingBuilder的方法不同步,但与其他string相比,Stringbuilder运行速度最快。 你可以通过实现它们来学习String,StringBuilder和StringBuffer之间的区别。
-------------------------------------------------- -------------------------------- String StringBuffer StringBuilder -------------------------------------------------- -------------------------------- 存储区| 常量string池堆堆 可修改| 否(不可变)是(可变)是(可变) 线程安全| 是是不是 性能| 快速非常慢速 -------------------------------------------------- --------------------------------