`
xzuse
  • 浏览: 43467 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

(转)Java 中String 与StringBuffer之间的区别 (网络搜集)

    博客分类:
  • Java
阅读更多

 

String的长度是不可变的,StringBuffer的长度是可变的。如果你对字符串中的内容经常进行操作,特别是内容要修改时,那么使用StringBuffer,如果最后需要String,那么使用StringBuffer的toString()方法

String和StringBuffer之概览
非可变对象一旦创建之后就不能再被改变,可变对象则可以在创建之后被改变。String对象是 非可变对象,StringBuffer对象则是可变对象。为获得更佳的性能你需要根据实际情况小心谨慎地选择到底使用这两者中的某一个。下面的话题会作详 细的阐述。(注意:这个章节假设读者已经具备Java的String和StringBuffer的相关基础知识。)
 
创建字符串的较佳途径
你可以按照以下方式创建字符串对象:
1. String s1 = "hello"; 
    String s2 = "hello"; 
2. String s3 = new String("hello");
    String s4 = new String("hello");
 
上面哪种方式会带来更好的性能呢?下面的代码片断用来测量二者之间的区别。

StringTest1.java
 package com.performance.string;

/**
 * This class shows the time taken for creation of String literals and String
 * objects.
 */
public class StringTest1 {
	public static void main(String[] args) {
		// create String literals
		long startTime = System.currentTimeMillis();
		for (int i = 0; i < 50000; i++) {
			String s1 = "hello";
			String s2 = "hello";
		}
		long endTime = System.currentTimeMillis();
		System.out.println("Time taken for creation of String literals : "
				+ (endTime - startTime) + " milli seconds");
		// create String objects using 'new' keyword
		long startTime1 = System.currentTimeMillis();
		for (int i = 0; i < 50000; i++) {
			String s3 = new String("hello");
			String s4 = new String("hello");
		}
		long endTime1 = System.currentTimeMillis();
		System.out.println("Time taken for creation of String objects : "
				+ (endTime1 - startTime1) + " milli seconds");
	}
}

 
这段代码的输出:
Time taken for creation of String literals : 0 milli seconds
Time taken for creation of String objects : 170 milli seconds
 
JVM是怎样处理字符串的呢?
Java虚拟机会维护一个内部的滞留字符串对象的列表(唯一字符串的池)来避免在堆内存中产生重复的String对象。当JVM从class文件里加载 字符串字面量并执行的时候,它会先检查一下当前的字符串是否已经存在于滞留字符串列表,如果已经存在,那就不会再创建一个新的String对象而是将引用 指向已经存在的String对象,JVM会在内部为字符串字面量作这种检查,但并不会为通过new关键字创建的String对象作这种检查。当然你可以明 确地使用String.intern()方法强制JVM为通过 new关键字创建的String对象作这样的检查。这样可以强制JVM检查内部列表而使用 已有的String对象。
所以结论是,JVM会内在地为字符串字面量维护一些唯一的String对象,程序员不需要为字符串字面量而发愁,但 是可能会被一些通过 new关键字创建的String对象而困扰,不过他们可以使用intern()方法来避免在堆内存上创建重复的String对象来改 善Java的运行性能。下一小节会向大家展示更多的信息。
 
下图展示了未使用intern()方法来创建字符串的情况。
 
string_creating_without_intern() method
你可以自己使用==操作符和String.equals()方法来编码测试上面提到的区别。==操作符会返回true如果一些引用指向一个相同的对象但 不会判断String对象的内容是否相同;String.equals()方法会返回true如果被操作的String对象的内容相同。对于上面的代码会 有s1==s2,因为s1和s2两个引用指向同一个对象,对于上面的代码,s3.equals(s4)会返回true因为两个对象的内容都一样为” hello”。你可以从上图看出这种机制。在这里有三个独立的包含了相同的内容(”hello”)的对象,实际上我们不需要这么三个独立的对象—— 因为 要运行它们的话既浪费时间又浪费内存。
 
那么怎样才能确保String对象不会重复呢?下一个话题会涵盖对于内建String机制的兴趣。
 
滞留字符串的优化作用
同一个字符串对象被重复地创建是不必要的,String.intern ()方法可以避免这种情况。下图说明了String.intern()方法是如 何工作的,String.intern()方法检查字符串对象的存在性,如果需要的字符串对象已经存在,那么它会将引用指向已经存在的字符串对象而不是重 新创建一个。下图描绘了使用了intern()方法的字符串字面量和字符串对象的创建情况。
 
string_creating_with_intern() method
下面的例程帮助大家了解String.intern()方法的重要性。
StringTest2.java

// This class shows the use of intern() method to improve performance
public class StringTest2 {
	public static void main(String[] args) {
		// create String references like s1,s2,s3...so on..
		String variables[] = new String[50000];
		for (int i = 0; i < variables.length; i++) {
			variables[i] = "s" + i;
		}
		// create String literals
		long startTime0 = System.currentTimeMillis();
		for (int i = 0; i < variables.length; i++) {
			variables[i] = "hello";
		}
		long endTime0 = System.currentTimeMillis();
		System.out.println("Time taken for creation of String literals : "
				+ (endTime0 - startTime0) + " milli seconds");
		// create String objects using 'new' keyword
		long startTime1 = System.currentTimeMillis();
		for (int i = 0; i < variables.length; i++) {
			variables[i] = new String("hello");
		}
		long endTime1 = System.currentTimeMillis();
		System.out
				.println("Time taken for creation of String objects with 'new' key word : "
						+ (endTime1 - startTime1) + " milli seconds");
		// intern String objects with intern() method
		long startTime2 = System.currentTimeMillis();
		for (int i = 0; i < variables.length; i++) {
			variables[i] = new String("hello");
			variables[i] = variables[i].intern();
		}
		long endTime2 = System.currentTimeMillis();
		System.out
				.println("Time taken for creation of String objects with intern(): "
						+ (endTime2 - startTime2) + " milli seconds");
	}
}

 
这是上面那段代码的输出结果:
Time taken for creation of String literals : 0 milli seconds
Time taken for creation of String objects with 'new' key word : 160 milli seconds
Time taken for creation of String objects with intern(): 60 milli seconds
 
连接字符串时候的优化技巧
你可以使用+操作符或者String.concat()或者StringBuffer.append()等办法来连接多个字符串,那一种办法具有最佳的性能呢?
如何作出选择取决于两种情景,第一种情景是需要连接的字符串是在编译期决定的还是在运行期决定的,第二种情景是你使用的是 StringBuffer还 是String。通常程序员会认为StringBuffer.append()方法会优于+操作符或 String.concat()方法,但是在一些特 定的情况下这个假想是不成立的。
 
1) 第一种情景:编译期决定相对于运行期决定
请看下面的StringTest3.java代码和输出结果。

package com.performance.string;

/**
 * This class shows the time taken by string concatenation at compile time and
 * run time.
 */
public class StringTest3 {
	public static void main(String[] args) {
		// Test the String Concatination
		long startTime = System.currentTimeMillis();
		for (int i = 0; i < 5000; i++) {
			String result = "This is" + "testing the" + "difference"
					+ "between" + "String" + "and" + "StringBuffer";
		}
		long endTime = System.currentTimeMillis();
		System.out
				.println("Time taken for string concatenation using + operator : "
						+ (endTime - startTime) + " milli seconds");
		// Test the StringBuffer Concatination
		long startTime1 = System.currentTimeMillis();
		for (int i = 0; i < 5000; i++) {
			StringBuffer result = new StringBuffer();
			result.append("This is");
			result.append("testing the");
			result.append("difference");
			result.append("between");
			result.append("String");
			result.append("and");
			result.append("StringBuffer");
		}
		long endTime1 = System.currentTimeMillis();
		System.out
				.println("Time taken for String concatenation using StringBuffer : "
						+ (endTime1 - startTime1) + " milli seconds");
	}
}

 
这是上面的代码的输出结果:
Time taken for String concatenation using + operator : 0 milli seconds
Time taken for String concatenation using StringBuffer : 50 milli seconds
很有趣地,+操作符居然比StringBuffer.append()方法要快,为什么呢?
 
这里编译器的优化起了关键作用,编译器像下面举例的那样简单地在编译期连接多个字符串。它使用编译期决定取代运行期决定,在你使用new关键字来创建String对象的时候也是如此。
 
编译前:
String result = "This is"+"testing the"+"difference"+"between"+"String"+"and"+"StringBuffer";
编译后:
String result = "This is testing the difference between String and StringBuffer";

这里String对象在编译期就决定了而StringBuffer对象是在运行期决定的。运行期决定需要额外的开销当字符串的值无法预先知道的时候,编译期决定作用于字符串的值可以预先知道的时候,下面是一个例子。
 
编译前:
public String getString(String str1,String str2) {
    return str1+str2;
}
编译后:
return new StringBuffer().append(str1).append(str2).toString();
运行期决定需要更多的时间来运行。
 
2) 第二种情景:使用StringBuffer取代String
看看下面的代码你会发现与情景一相反的结果——连接多个字符串的时候StringBuffer要比String快。
StringTest4.java
 package com.performance.string;

/**
 * This class shows the time taken by string concatenation using + operator and
 * StringBuffer
 */
public class StringTest4 {
	public static void main(String[] args) {
		// Test the String Concatenation using + operator
		long startTime = System.currentTimeMillis();
		String result = "hello";
		for (int i = 0; i < 1500; i++) {
			result += "hello";
		}
		long endTime = System.currentTimeMillis();
		System.out
				.println("Time taken for string concatenation using + operator : "
						+ (endTime - startTime) + " milli seconds");
		// Test the String Concatenation using StringBuffer
		long startTime1 = System.currentTimeMillis();
		StringBuffer result1 = new StringBuffer("hello");
		for (int i = 0; i < 1500; i++) {
			result1.append("hello");
		}
		long endTime1 = System.currentTimeMillis();
		System.out
				.println("Time taken for string concatenation using StringBuffer :  "
						+ (endTime1 - startTime1) + " milli seconds");
	}
}

 
这是上面的代码的输出结果:
Time taken for string concatenation using + operator : 280 milli seconds
Time taken for String concatenation using StringBuffer : 0 milli seconds
看得出StringBuffer.append()方法要比+操作符要快得多,为什么呢?

原因是两者都是在运行期决定字符串对象,但是+操作符使用不同于StringBuffer.append()的规则通过String和StringBuffer来完成字符串连接操作。(译注:什么样的规则呢?)
 
借助StringBuffer的初始化过程的优化技巧
你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。这里提到的构造函数是StringBuffer (int length),length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity (int minimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行 为,然后再找出一条更好的提升性能的途径。
 
StringBuffer的缺省行为:
StringBuffer在内部维护一个 字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,因为没有设置初始化字符长度,StringBuffer的容量被初始化为 16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值 +2)。
如果你使用缺省值,初始化之后接着往里面追加字符,在你追加到第16个字符的时候它会将容量增加到34(2*16+2),当追加到 34个字符的时候就会将容量增加到70(2*34+2)。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新 将旧字符和新字符都拷贝一遍——这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来立竿见影的性能增 益。
我利用两个StringBuffer重新测试了上面的StringTest4.java代码,一个未使用初始化容量值而另一个使用了。这 次我追加了50000个’hello’对象没有使用+操作符。区别是我使用StringBuffer(250000)的构造函数来初始化第二个  StringBuffer了。
 
输出结果如下:
Time taken for String concatenation using StringBuffer with out setting size: 280 milli seconds
Time taken for String concatenation using StringBuffer with setting size: 0 milli seconds
StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。
 
关键点
1. 无论何时只要可能的话使用字符串字面量来常见字符串而不是使用new关键字来创建字符串。
2. 无论何时当你要使用new关键字来创建很多内容重复的字符串的话,请使用String.intern()方法。
3. +操作符会为字符串连接提供最佳的性能——当字符串是在编译期决定的时候。
4. 如果字符串在运行期决定,使用一个合适的初期容量值初始化的StringBuffer会为字符串连接提供最佳的性能。 

 

 

 

分享到:
评论

相关推荐

    Java中String和StringBuffer的区别.doc

    在Java编程语言中,String和StringBuffer是两个重要的类,它们在处理文本数据时有着显著的区别。String类代表不可变的字符序列,一旦创建,其值就不能改变。这意味着每次对String对象进行修改(如拼接操作)都会创建...

    Java 中String StringBuilder 与 StringBuffer详解及用法实例

    在Java编程语言中,String、StringBuilder和StringBuffer都是用来处理字符串的重要类,它们各有特点,适用于不同的场景。这里我们将深入探讨这三个类的区别、特性和使用策略。 首先,String类是不可变的,这意味着...

    Java类库复习——java.lang.String

    `String`类与`StringBuilder`和`StringBuffer`的区别在于,后两者是可变的,适合在循环中进行字符串拼接,因为每次调用`+`运算符或`concat()`方法时,`String`都会创建新的对象,这在大量操作时可能会导致性能下降。...

    java工具类(String)

    Java中的字符串处理是编程工作中非常常见且重要的任务,而String工具类则提供了许多方便实用的方法,使得我们在处理字符串时能够更加高效。以下是对Java String工具类的详细解析: 首先,了解Java中的String类。在...

    java面试笔试题大汇总

    包括Java面试题目、多线程编程、通信编程、数据库查询、异常处理机制、事件机制、多形与继承、抽象类与接口、String与StringBuffer的区别、final、finally的区别、Collection和Collections的区别、GC(垃圾收集器)...

    区别Java中堆与栈区别Java中堆与栈

    Java 中堆与栈的区别 Java 中的堆和栈是两个不同的内存区域,分别用于存放不同类型的数据。堆是一个运行时数据区,类的对象从中分配空间,通过new、newarray、anewarray 和 multianewarray 等指令建立,垃圾回收器...

    Java面试题大全.pdf

    - Java中String类的不可变性以及String、StringBuffer和StringBuilder的区别和使用场景。 3. Java IO流 - Java IO流的分类,包括字节流(如InputStream和OutputStream)和字符流(如Reader和Writer)。 - Java中...

    Java基础知识点 - 内容比较全面

    9. **Java中String和StringBuffer的区别**:String是不可变对象,每次修改都会创建新对象;StringBuffer是可变的,适合在多线程环境中进行字符串拼接,避免频繁的对象创建。 10. **Java中Comparable和Comparator...

    JAVA面试题解惑系列(六)——字符串(String)杂谈[收集].pdf

    总的来说,理解和掌握`String`类的`length()`方法、字符数组与字符串的区别以及Unicode编码在Java中的应用,是Java程序员必备的基础技能,也是面试中常见的问题。对于处理中文字符,需要特别注意字符编码的细节,以...

    java常用字符串方法,网络收集,txt版

    本资源"java常用字符串方法,网络收集,txt版"主要整理了Java中常用的字符串处理方法,这对于Java程序员来说是一份宝贵的参考资料。下面,我们将详细探讨这些方法,并结合实例进行讲解。 1. **创建字符串** - `...

    java常用字符串方法网络收集txt版

    字符串类`String`在Java中有着丰富的内置方法,使得开发者能够高效地处理字符串。本资料"java常用字符串方法网络收集txt版"很可能是对这些常用方法的集合整理,旨在帮助Java程序员快速查找和理解相关功能。 1. **...

    深入理解Java:String

    Java中的字符串(String)是编程中不可或缺的部分,深入理解其内在机制对于提高代码性能至关重要。本文将探讨Java中字符串的内存管理,尤其是与Java虚拟机(JVM)内存模型的关联。 首先,Java内存模型分为堆内存...

    Java常用类库StringBuffer,Runtime,日期操作类等类库总结

    StringBuffer类是Java中一个可变的字符串类,与String类不同的是,StringBuffer类的内容可以改变。StringBuffer类中的方法都是同步的,属于安全的线程操作。相比之下,StringBuilder类中的方法都是异步的,属于非...

    java面试题收集集锦

    Java面试题集锦涵盖了广泛的Java基础知识,JSP与Servlet技术,J2EE相关知识以及其他一些IT领域的要点。以下是对这些知识点的详细说明: 1. **Java基础** - Java有八种基本数据类型:byte, short, int, long, float...

    java面试题及答案

    1. **对象与引用的区别**:在Java中,对象是类的一个实例,它包含数据成员(即类的属性)和方法。而引用则是在内存中存储的对象地址,通过这个地址可以访问到对象。当一个对象被创建时,它会被分配一块内存空间,并...

    Java堆和栈的区别

    "Java 堆和栈的区别" Java 堆和栈是 Java 中的两种内存管理机制,它们都是 Java 用来在 RAM 中存放数据的地方。但是,它们有很多不同之处。 Java 堆是一个运行时数据区,类的对象从中分配空间。这些对象通过 new、...

    StringTable.pdf

    1. **G1中的去重操作**:G1垃圾收集器在处理StringTable时,会进行字符串去重,减少内存占用。 2. **回收效率**:由于永久代的回收频率低,将StringTable移至堆空间后,可以更及时地进行垃圾回收,适应现代Java应用...

    java中120个经典问

    以上只是Java学习中的冰山一角,实际开发中还会涉及到异常处理、并发编程、IO流、网络编程、反射机制、垃圾收集、设计模式等众多主题。深入理解这些知识点,对于成为一名优秀的Java开发者至关重要。

    面试大连花旗的Java面试题必看.docx

    4. **String与StringBuffer的区别** - **String**对象一旦创建,其内容就不能更改,每次修改都会创建新的对象,消耗资源。适用于字符串常量的存储。 - **StringBuffer**(或StringBuilder)提供可变字符串,内部...

Global site tag (gtag.js) - Google Analytics