【发布时间】:2011-02-27 14:48:34
【问题描述】:
请告诉我一个比较String、StringBuffer和StringBuilder的实时情况?
【问题讨论】:
标签: java string oop stringbuilder
请告诉我一个比较String、StringBuffer和StringBuilder的实时情况?
【问题讨论】:
标签: java string oop stringbuilder
你的意思是连接吗?
现实世界的例子:你想从许多其他字符串中创建一个新字符串。
例如发送消息:
字符串
String s = "Dear " + user.name + "<br>" +
" I saw your profile and got interested in you.<br>" +
" I'm " + user.age + "yrs. old too"
字符串生成器
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 字符串连接
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 在赋值右侧进行字符串连接通常不是一个好习惯。正如您所说,任何好的实现都会在幕后使用 StringBuilder 。此外,您的"a" + "b" 示例将被编译为单个文字"ab",但如果您使用StringBuilder,则会导致对append() 的两次不必要的调用。
"a"+"b",而是要说什么是字符串连接,我将其更改为明确的。你不说的是,为什么这样做不是一个好习惯。这正是(现代)编译器所做的。 @fuzzy,我同意,特别是当您知道最终字符串的大小是多少时( aprox )。
另外,StringBuffer 是线程安全的,StringBuilder 不是。
因此,在不同线程访问它的实时情况下,StringBuilder 可能会产生不确定的结果。
【讨论】:
String;从String 获取新的字符序列可能会在 CPU 时间或内存方面带来不可接受的性能损失(获取子字符串是 CPU 高效的,因为数据不会被复制,但这意味着可能会保留更多的数据量) .StringBuilder,通常用于将多个字符序列连接在一起。StringBuilder 相同的情况下使用StringBuffer,但必须同步对基础字符串的更改(因为多个线程正在读取/修改字符串缓冲区)。查看示例here。
【讨论】:
可变性差异:
String 是不可变的,如果您尝试更改它们的值,则会创建另一个对象,而 StringBuffer 和 StringBuilder 是可变的,因此它们可以更改他们的价值观。
线程安全差异:
StringBuffer 和StringBuilder 的区别在于StringBuffer 是线程安全的。因此,当应用程序只需要在单个线程中运行时,最好使用StringBuilder。 StringBuilder 比 StringBuffer 更高效。
情况:
String 对象是不可变的。StringBuilder 就足够了。StringBuffer,因为StringBuffer 是同步的,因此您具有线程安全性。【讨论】:
Strings 中,当我们更改值时,会创建另一个对象。旧对象引用是否被无效,以便它可能被 GC 垃圾收集,或者甚至被垃圾收集?
String 和StringBuilder 的所有方法吗?
基础知识:
String 是一个不可变的类,它不能被改变。
StringBuilder 是一个可变类,可以附加、替换或删除字符并最终转换为 String
StringBuffer是StringBuilder的原始同步版本
在您只有一个线程访问您的对象的所有情况下,您应该更喜欢StringBuilder。
详情:
还要注意StringBuilder/Buffers 不是魔术,它们只是使用一个数组作为支持对象,并且当数组满时必须重新分配它。确保创建足够大的 StringBuilder/Buffer 对象,这样它们就不必在每次调用 .append() 时都不断地重新调整大小。
重新调整大小会变得非常退化。每次需要扩展时,它基本上都会将支持 Array 的大小重新调整为当前大小的 2 倍。当StringBuilder/Buffer 类开始变大时,这可能会导致分配大量 RAM 而未被使用。
在 Java 中 String x = "A" + "B"; 在幕后使用 StringBuilder。因此,对于简单的情况,声明自己的情况没有任何好处。但是,如果您正在构建较大的 String 对象,例如小于 4k,则声明 StringBuilder sb = StringBuilder(4096); 比连接或使用只有 16 个字符的 default constructor 更有效。如果您的 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);
}
如果您不知道String 的大小,但您可以猜到,最好将StringBuilder/Buffer 初始化为比您认为需要的大一点。一次分配比您需要的稍多一点的内存会比大量的重新分配和复制要好。
还要注意用String 初始化StringBuilder/Buffer,因为这只会分配字符串+ 16 个字符的大小,在大多数情况下,这只会开始您尝试的退化重新分配和复制周期避免。以下直接来自 Java 6 源代码。
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
如果您碰巧最终得到了StringBuilder/Buffer 的实例,而该实例不是您创建的并且无法控制被调用的构造函数,那么有一种方法可以避免退化的重新分配和复制行为。调用 .ensureCapacity() 并使用您想要确保生成的 String 的大小。
替代方案:
请注意,如果您确实繁重 String 构建和操作,还有一个更注重性能的替代方案,称为Ropes。
另一种选择是通过子类化ArrayList<String> 创建StringList 实现,并添加计数器以跟踪每个.append() 上的字符数以及列表的其他变异操作,然后覆盖.toString() 以创建一个您需要的确切大小的StringBuilder 并遍历列表并构建输出,您甚至可以将StringBuilder 设为实例变量并“缓存”.toString() 的结果,并且只需要重新生成它当事情发生变化时。
在构建固定格式的输出时不要忘记String.format(),编译器可以对其进行优化,因为它们会变得更好。
【讨论】:
String x = "A" + "B"; 真的编译成 StringBuilder 吗?为什么不直接编译为String x = "AB";,如果组件在编译时未知,它应该只使用StringBuilder。
请注意,如果您使用的是 Java 5 或更新版本,则应使用 StringBuilder 而不是 StringBuffer。来自 API 文档:
从 JDK 5 开始,这个类已经补充了一个为单线程设计的等效类,
StringBuilder。StringBuilder类通常应该优先于这个类,因为它支持所有相同的操作,但速度更快,因为它不执行同步。
实际上,您几乎不会同时在多个线程中使用它,因此StringBuffer 所做的同步几乎总是不必要的开销。
【讨论】:
就个人而言,我认为StringBuffer 没有任何实际用途。我什么时候想通过操作字符序列在多个线程之间进行通信?这听起来一点用都没有,但也许我还没有看到光明:)
【讨论】:
在 java 中,String 是不可变的。不可变意味着一旦创建了 String ,我们就不能改变它的值。 StringBuffer 是可变的。创建 StringBuffer 对象后,我们只需将内容附加到对象的值,而不是创建新对象。 StringBuilder 类似于 StringBuffer 但它不是线程安全的。 StingBuilder 的方法不同步,但与其他字符串相比,Stringbuilder 运行得最快。 您可以通过实现它们来了解String, StringBuilder and StringBuffer 之间的区别。
【讨论】:
String与其他两个类的区别在于String是不可变的,另外两个是可变类。
但为什么我们有两个用于相同目的的类?
原因是StringBuffer 是线程安全的,而StringBuilder 不是。
StringBuilder 是 StringBuffer Api 上的一个新类,它是在 JDK5 中引入的,如果您在单线程环境中工作,总是推荐使用它,因为它很多 Faster
【讨论】:
【讨论】:
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 中的每个方法都是同步的,因此是线程安全的。
StringBuffer 和 StringBuilder 数据只能使用 new 运算符创建。因此,它们被存储在堆内存中。
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);
如果您打算每次都修改值,请使用比 StringBuffer 更快的 StringBuilder。如果多个线程正在修改 StringBuffer 的值。
【讨论】: