字符串、字符串缓冲区和字符串构建器的区别与性能对比分析
这是文章《字符串 vs 字符串缓冲区 vs 字符串构建器》的第1部分(共1部分)。
内容片段: 字符串是Java中最广泛使用的类之一。字符串缓冲区(StringBuffer)和字符串构建器(StringBuilder)类提供了操作字符串的方法。我们将探讨字符串缓冲区和字符串构建器之间的区别。字符串缓冲区和字符串构建器之间的区别是一个常见的Java面试问题。
字符串 vs 字符串缓冲区 vs 字符串构建器
字符串是核心Java面试中最重要的主题之一。如果你正在编写一个在控制台打印内容的程序,你就会使用字符串。本教程旨在重点讨论字符串类的主要特性。然后,我们将比较字符串缓冲区(StringBuffer)和字符串构建器(StringBuilder)类。
Java中的字符串
字符串(String)类表示字符序列,我们可以有两种方式来实例化字符串。
String str = "ABC";
// 或者
String str = new String("ABC");
在Java中,字符串是不可变的。所以在多线程环境中使用它是适当的。我们可以在函数之间共享它,因为不存在数据不一致的问题。
当我们使用双引号创建一个字符串时,JVM首先在字符串池中查找具有相同值的字符串。如果找到了,它将返回字符串池中字符串对象的引用。否则,它将在字符串池中创建字符串对象并返回引用。通过在不同的线程中使用相同的字符串,JVM可以节省大量的内存。
如果使用new运算符创建字符串,它将在堆内存中创建。
+运算符对字符串进行了重载。我们可以使用它来连接两个字符串。尽管内部实际上使用字符串缓冲区(StringBuffer)来执行此操作。
字符串覆盖了equals()和hashCode()方法。仅当两个字符串具有相同的字符序列时它们才相等。equals()方法区分大小写。如果你需要进行大小写不敏感的检查,应该使用equalsIgnoreCase()方法。
字符串使用UTF-16编码进行字符流处理。
字符串是一个final类。除了”private int hash”之外,所有字段都是final的。该字段包含hashCode()函数的值。hashcode的值只有在第一次调用hashCode()方法时才会计算,并在该字段中被缓存。此外,hash是使用字符串类的final字段进行一些计算生成的。因此,每次调用hashCode()方法时,都会得到相同的输出。对于调用者来说,似乎每次都进行计算,但实际上它被缓存在hash字段中。
字符串 vs 字符串缓冲区
由于Java中的字符串是不可变的,所以无论是进行字符串的连接、子字符串等操作,都会生成一个新的字符串对象,并丢弃旧的字符串对象以便进行垃圾回收。这些操作会消耗大量的系统资源并在堆中产生大量的垃圾。为此,Java提供了字符串缓冲区(StringBuffer)和字符串构建器(StringBuilder)类来进行字符串操作。字符串缓冲区和字符串构建器是可变的对象,在Java中使用它们来进行字符串操作。它们提供了append()、insert()、delete()和substring()等方法。
字符串缓冲区和字符串构建器的区别
直到Java 1.4之前,字符串缓冲区(StringBuffer)是处理字符串操作的唯一选择。然而,它有一个缺点,即其所有公共方法都是同步的。字符串缓冲区提供了线程安全性,但却降低了性能。在大多数情况下,我们并不需要在多线程环境中使用字符串。因此,Java 1.5引入了一个新类字符串构建器(StringBuilder),它与字符串缓冲区类似,除了线程安全和同步之外。字符串缓冲区拥有一些额外的方法,例如substring、length、capacity、trimToSize等。然而,这些方法在字符串中也都已经存在,因此在字符串构建器类中没有全部实现。字符串缓冲区在Java 1.0中引入,而字符串构建器类在Java 1.5中引入,针对字符串缓冲区的一些缺点进行了改进。如果您在单线程环境中操作或者不关心线程安全,应该使用字符串构建器。否则,对于需要线程安全操作的情况,请使用字符串缓冲区。
字符串构建器(StringBuilder)与字符串缓冲区(StringBuffer)的性能对比
我正在尝试检查由于与一个示例程序中的字符串缓冲区(StringBuffer)和字符串构建器(StringBuilder)对象进行多次append()操作而导致的性能影响。
package com.Olivia.java;
import java.util.GregorianCalendar;
public class TestString {
public static void main(String[] args) {
System.gc();
long start=new GregorianCalendar().getTimeInMillis();
long startMemory=Runtime.getRuntime().freeMemory();
StringBuffer sb = new StringBuffer();
//StringBuilder sb = new StringBuilder();
for(int i = 0; i<10000000; i++){
sb.append(":").append(i);
}
long end=new GregorianCalendar().getTimeInMillis();
long endMemory=Runtime.getRuntime().freeMemory();
System.out.println("Time Taken:"+(end-start));
System.out.println("Memory used:"+(startMemory-endMemory));
}
}
我也对字符串缓冲区对象运行了相同的代码,以检查时间和内存值。我对每个案例执行了5次代码,然后计算了平均值。
i的值 | 字符串缓冲区 (时间, 内存) | 字符串构建器 (时间, 内存) |
---|---|---|
10,00,000 | 808, 149356704 | 633, 149356704 |
1,00,00,000 | 7448, 147783888 | 6179, 147783888 |
很明显,在单线程环境中,字符串构建器的性能比字符串缓冲区要好。这种性能差异可以归因于字符串缓冲区方法中的同步操作。
字符串 vs. 字符串缓冲区 vs. 字符串构建器
- 字符串是不可变的,而字符串缓冲区(StringBuffer)和字符串构建器(StringBuilder)是可变的类。
- 字符串缓冲区是线程安全且同步的,而字符串构建器不是。这就是为什么字符串构建器比字符串缓冲区更快的原因。
- 字符串连接操作符(+)内部使用了字符串缓冲区或字符串构建器类。
- 在非多线程环境中进行字符串操作时,应使用字符串构建器;否则使用字符串缓冲区类。
这就是有关字符串、字符串缓冲区和字符串构建器差异的快速总结。在大多数一般的编程场景中,字符串构建器比字符串缓冲区更适用。
参考资料:
- 字符串(String) API文档
- 字符串缓冲区(StringBuffer) API文档
- 字符串构建器(StringBuilder) API文档