Java基础知识中字符串的使用场景的极其性能研究
Posted Ps_Q
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java基础知识中字符串的使用场景的极其性能研究相关的知识,希望对你有一定的参考价值。
今天闲来无事,来研究一些Java基础知识中字符串的使用场景的极其性能
先声明,这篇有点不适合新手,适合有基础的兄弟
首当其冲的是字符串类型String,StringBuffer,StringBuilder。
我们都知道String是不可变的StringBuffer和StringBuilder是可变的,但是其中的一些性能细节可能还没去测试过,我们可以看一下JDK里面三个类的构造方法
String定义属性和构造方法:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
public String() {
this.value = "".value;
}
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
StringBuilder源码:
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
public StringBuilder() {
super(16);
}
public StringBuilder(int capacity) {
super(capacity);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
StringBuffer源码:
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
private transient char[] toStringCache;
public StringBuffer() {
super(16);
}
从JDK源码看,String、StringBuilder、StringBuffer都是存放在char[] 数组字符串。但String 中定义的char[] 数组是用final 修饰,所以,String 是不可变字符序列,而StringBuilder和StringBuffer是可变字符序列;
所以Sting 需要改变则需要重新创建新对象;而StringBuffer 和 StringBuilder 都继承 AbstractStringBuilder类,他们在初始化时,都是调用父类的构造器。
那我们看一下AbstractStringBuilder类的源码:
abstract class AbstractStringBuilder implements Appendable, CharSequence {
/**
* The value is used for character storage.
*/
char[] value;
/**
* The count is the number of characters used.
*/
int count;
/**
* This no-arg constructor is necessary for serialization of subclasses.
*/
AbstractStringBuilder() {
}
/**
* Creates an AbstractStringBuilder of the specified capacity.
*/
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
其实里面也是定义了一个char[]数组字符串。但AbstractStringBuilder 的char[]是可变的,所以StringBuffer和StringBuilder是可变的
网上有一张图,关于三种字符串类型的介绍,其实这些内容也可以在一些这几个类的常用方法中找出,有兴趣的话可以去看看这几个类的常用方法源码和扩展方法源码。
那么现在情况大概知道了,我们怎么去看到底这三个类是什么样的呢?是骡子是马拉出来溜溜。
这是我大晚上敲的还热乎着的代码。
public class StringTest {
private static final int CODE = 100000;
static private void basedOnString() {
long beginTime = System.currentTimeMillis();
String str = "";
for (int i = 0; i < CODE; i++) {
str = str + i;
}
long endTime = System.currentTimeMillis();
System.out.println("测试Sting执行时间:" + (endTime - beginTime));
}
static private void baseOnStringBuffer() {
long beginTime = System.currentTimeMillis();
StringBuffer buffer = new StringBuffer("");
for (int i = 0; i < CODE; i++) {
buffer.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("测试StringBuffer执行时间:" + (endTime - beginTime));
}
static private void baseOnStringBuilder() {
long beginTime = System.currentTimeMillis();
StringBuilder builder = new StringBuilder("");
for (int i = 0; i < CODE; i++) {
builder.append(i);
}
long endTime = System.currentTimeMillis();
System.out.println("测试StringBuilder执行时间:" + (endTime - beginTime));
}
public static void main(String[] args) {
System.out.println("测试字符类型赋值效率,遍历次数"+CODE+"次");
basedOnString();
baseOnStringBuffer();
baseOnStringBuilder();
}
}
运行这段代码的最终结果是
测试字符类型赋值效率,遍历次数100000次
测试Sting执行时间:25390
测试StringBuffer执行时间:7
测试StringBuilder执行时间:5
总结:String是无法改变的,在更改值的时候会重新创建一个String, StringBuffer和StringBuilder是可以改变的,StringBuilder是最快的,但是StringBuffer线程是安全的,StringBuilder线程是不安全的,如果考虑到线程安全, 建议使用StringBuffer, 如果在单线程或者不考虑线程安全的情况下,可以使用StringBuilder追求效率。
但是有不少人发现在数量太少的情况下,StringBuilder 在 StringBuffer加锁的情况下,并没有体现出优势,反而StringBuffer 更胜一筹。
其实是因为StringBuffer比StringBuilder多了一个缓冲区,我们看下StringBuffer的toString方法:
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
再看看StringBuilder的toString方法:
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
其实可以发现StringBuffer的缓存有数据时,就直接在缓存区取,而StringBuilder每次都是直接copy。这样StringBuffer 相对StringBuilder来说其实是做了一个性能上的优化,所有只有当数量足够大,StringBuffer的缓冲区填补不了加锁影响的性能时,StringBuilder才在性能上展现出了它的优势。
差不多就是这么个情况了,白天测了测,晚上看源码不容易,早点睡觉,不然掉头发可就裂开了。
以上是关于Java基础知识中字符串的使用场景的极其性能研究的主要内容,如果未能解决你的问题,请参考以下文章