Answers:
变异性差异:
String
是不可变的,如果您尝试更改其值,则会创建另一个对象,而StringBuffer
和StringBuilder
是可变的,因此它们可以更改其值。
线程安全差异:
之间的区别StringBuffer
,并StringBuilder
是StringBuffer
是线程安全的。因此,当应用程序只需要在单个线程中运行时,最好使用StringBuilder
。StringBuilder
比效率高StringBuffer
。
情况:
String
对象是不可变的。StringBuilder
就足够了。StringBuffer
因为它StringBuffer
是同步的,所以您具有线程安全性。Strings
当我们更改值时,将创建另一个对象。旧对象引用是否为空,以便它可能是的垃圾回收,GC
甚至是垃圾回收?
String
with的所有方法StringBuilder
吗?
String
在不可变结构适用的情况下使用;从String
CPU 获得新的字符序列可能会在CPU时间或内存上带来不可接受的性能损失(获得子字符串对于CPU来说是有效的,因为未复制数据,但这意味着可能会保留大量的数据)。StringBuilder
,当你需要创建一个可变的字符序列,通常是连接几个字符序列在一起。StringBuffer
你会使用相同的情况下StringBuilder
,但是当改变底层字符串必须是同步的(因为有几个线程读取/ modifyind字符串缓冲区)。在这里查看示例。
基础:
String
是一个不可变的类,无法更改。
StringBuilder
是一个可变类,可以将其追加,替换或删除字符,并最终将其转换String
StringBuffer
为的原始同步版本StringBuilder
StringBuilder
在所有只有单个线程访问对象的情况下,您都应该首选。
细节:
还要注意,StringBuilder/Buffers
这并不是魔术,它们只是将Array用作后备对象,并且一旦阵列满就必须重新分配。确保StringBuilder/Buffer
最初创建足够大的对象,而不必每次.append()
调用它们时都不断调整它们的大小。
调整大小可能会非常退化。基本上,每次需要扩展时,将后备阵列的大小调整为当前大小的2倍。当StringBuilder/Buffer
类开始变大时,这可能导致分配大量RAM而不使用它们。
在Java中String x = "A" + "B";
使用StringBuilder
幕后。因此,对于简单的情况,声明自己的名字没有任何好处。但是,如果要构建String
较大的对象(例如小于4k),则声明StringBuilder sb = StringBuilder(4096);
比串联或使用仅包含16个字符的默认构造函数要有效得多。如果String
小于10k,请使用构造函数将其初始化为10k,以确保安全。但是,如果将其初始化为10k,则您写入1个字符多于10k,它将被重新分配并复制到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
将要多大但可以猜测的话,将其初始化为比您认为需要的大一点的值。一种比您需要的内存稍微多一点的分配将比许多重新分配和复制要好。
另外注意的初始化的StringBuilder/Buffer
一个String
作为将只分配字符串+ 16个字符,在大多数情况下会刚开始堕落重新分配并复制周期,你正试图避免的大小。以下直接来自Java 6源代码。
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
如果您偶然遇到了一个StringBuilder/Buffer
您没有创建且无法控制被调用的构造函数的实例,那么有一种方法可以避免退化的重新分配和复制行为。致电.ensureCapacity()
您想要的尺寸,以确保结果String
适合您的尺寸。
替代方案:
请注意,如果您要进行大量的 String
构建和操作,则有一种名为Ropes的面向性能的替代品。
另一种选择是StringList
通过子类创建实现ArrayList<String>
,并添加计数器以跟踪.append()
列表的每个其他突变操作上的字符数,然后重写.toString()
以创建StringBuilder
所需大小的a 并遍历列表并构建在输出中,您甚至可以使StringBuilder
实例变量和“缓存”结果,.toString()
并且仅在发生更改时才重新生成它。
同样,String.format()
在构建固定格式的输出时也不要忘记,编译器可以对其进行优化,因为它们可以使输出更好。
String x = "A" + "B";
真的编译成为一个StringBuilder?为什么不将其编译为String x = "AB";
,如果在编译时不知道组件,则只能使用StringBuilder。
您的意思是串联吗?
实际示例: 您想从其他许多字符串中创建一个新字符串。
例如发送一条消息:
串
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
它将运行得更快,因为它不会停止查看它是否拥有线程锁。
因此,建议使用StringBuilder
(除非您当然有多个线程同时访问它,这种情况很少见)
String
串联(使用+运算符)可以由编译器优化以在StringBuilder
下面使用,因此,在Java的较早时期,不必再担心这一点了,每个人都应该不惜一切代价避免这样做,因为每次串联创建了一个新的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”的字符串
第31-53行代表名为“ builder”的字符串
疗法没有区别,完全一样的代码为两个字符串执行。
StringBuilder
,在分配的右侧使用a 进行String连接不是一个好习惯。如您所说,任何好的实现都将在后台使用StringBuilder。此外,您的示例"a" + "b"
将被编译为单个文字,"ab"
但如果使用StringBuilder
它,则会导致对的两个不必要的调用append()
。
"a"+"b"
而是要说什么是String串联,我将其更改为明确。您没有说的是,为什么这样做不是一个好习惯。这正是(现代的)编译器所做的。@fuzzy,我同意,特别是当您知道最终字符串的大小为(aprox)时。
-------------------------------------------------- -------------------------------- 字符串StringBuffer StringBuilder -------------------------------------------------- -------------------------------- 仓储区 常量字符串池堆 可修改的 否(不可变)是(可变)是(可变) 线程安全| 是是否 性能 快速非常慢快速 -------------------------------------------------- --------------------------------
synchronised
,这就是原因。
串
该String class
代表字符串。Java程序中的所有字符串文字(例如)"abc"
都作为此类的实例实现。
字符串对象一旦创建便是不可变的,我们无法更改。(字符串是常量)
如果使用构造器或方法创建了一个字符串,那么这些字符串将会被存储在堆内存中SringConstantPool
。但是在保存到池中之前,它会intern()
使用equals方法调用方法以检查池中具有相同内容的对象可用性。如果字符串复制在池中可用,则返回引用。否则,将String对象添加到池中并返回引用。
+
)以及将其他对象转换为字符串提供了特殊的支持。字符串连接是通过StringBuilder(或StringBuffer)类及其append方法实现的。String heapSCP = new String("Yash");
heapSCP.concat(".");
heapSCP = heapSCP + "M";
heapSCP = heapSCP + 777;
// For Example: String Source Code
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
字符串文字存储在中StringConstantPool
。
String onlyPool = "Yash";
StringBuilder和StringBuffer是可变的字符序列。这意味着可以更改这些对象的值。StringBuffer与StringBuilder具有相同的方法,但是StringBuffer中的每个方法都是同步的,因此是线程安全的。
只能使用new运算符创建StringBuffer和StringBuilder数据。因此,它们被存储在堆内存中。
StringBuilder实例不能安全地用于多个线程。如果需要这样的同步,则建议使用StringBuffer。
StringBuffer threadSafe = new StringBuffer("Yash");
threadSafe.append(".M");
threadSafe.toString();
StringBuilder nonSync = new StringBuilder("Yash");
nonSync.append(".M");
nonSync.toString();
StringBuffer和StringBuilder具有特殊方法,如。
replace(int start, int end, String str)
和reverse()
。
注意:StringBuffer和SringBuilder是可变的,因为它们提供的实现
Appendable Interface
。
什么时候使用哪个。
如果您不想每次都更改值,则最好使用String Class
。作为泛型的一部分,如果您想对Comparable<T>
值进行排序或比较,请使用String Class
。
//ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
Set<StringBuffer> set = new TreeSet<StringBuffer>();
set.add( threadSafe );
System.out.println("Set : "+ set);
如果您每次都要修改值,那么StringBuilder的使用要比StringBuffer快。如果多个线程正在修改该值,请使用StringBuffer。
就个人而言,我认为现实世界中没有任何用途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/
在Java中,String是不可变的。不可变是指一旦创建了字符串,就无法更改其值。 StringBuffer是可变的。创建StringBuffer对象后,我们只需将内容附加到对象的值,而不是创建新的对象。 StringBuilder与StringBuffer相似,但是它不是线程安全的。StingBuilder的方法不同步,但是与其他String相比,Stringbuilder运行最快。您可以通过实现它们来了解String,StringBuilder和StringBuffer之间的区别。