字符串、字符串缓冲区和字符串构建器的区别与性能对比分析

这是文章《字符串 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文档
bannerAds