java string 比较 效率_java中String StringBuilder StringBuffer比较和效率(性能)测试

string stringbuilder stringbuffer三者的区别

从JDK源码看,String、StringBuilder、StringBuffer都是存放在char[] 数组字符串。

简单看下三者的部分源码:

String定义属性和构造方法:

public final class String

implements java.io.Serializable, Comparable, 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);

}

比较明显的是:

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];

}

可以看到 AbstractStringBuilder 其实也定义了char[] 数组,不同的是,AbstractStringBuilder 中的char[] 数组可以可变的,在细看一点,可以看到AbstractStringBuilder 有扩容的方法:

private int newCapacity(int minCapacity) {

// overflow-conscious code

int newCapacity = (value.length << 1) + 2;

if (newCapacity - minCapacity < 0) {

newCapacity = minCapacity;

}

return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)

? hugeCapacity(minCapacity)

: newCapacity;

}

private int hugeCapacity(int minCapacity) {

if (Integer.MAX_VALUE - minCapacity < 0) { // overflow

throw new OutOfMemoryError();

}

return (minCapacity > MAX_ARRAY_SIZE)

? minCapacity : MAX_ARRAY_SIZE;

}

接下来我们继续,看下String 、StringBuffer 和 StringBuilder的常用方法:

String的常用方法:

public String substring(int beginIndex) {

if (beginIndex < 0) {

throw new StringIndexOutOfBoundsException(beginIndex);

}

int subLen = value.length - beginIndex;

if (subLen < 0) {

throw new StringIndexOutOfBoundsException(subLen);

}

return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);

}

public String substring(int beginIndex, int endIndex) {

if (beginIndex < 0) {

throw new StringIndexOutOfBoundsException(beginIndex);

}

if (endIndex > value.length) {

throw new StringIndexOutOfBoundsException(endIndex);

}

int subLen = endIndex - beginIndex;

if (subLen < 0) {

throw new StringIndexOutOfBoundsException(subLen);

}

return ((beginIndex == 0) && (endIndex == value.length)) ? this

: new String(value, beginIndex, subLen);

}

StringBuilder的常用方法:

@Override

public StringBuilder append(int i) {

super.append(i);

return this;

}

@Override

public StringBuilder append(long lng) {

super.append(lng);

return this;

}

@Override

public StringBuilder append(float f) {

super.append(f);

return this;

}

StringBuffer的常用方法:

@Override

public synchronized StringBuffer append(CharSequence s, int start, int end)

{

toStringCache = null;

super.append(s, start, end);

return this;

}

@Override

public synchronized StringBuffer append(char[] str) {

toStringCache = null;

super.append(str);

return this;

}

从它们的常用方法可以看出:

String 每次返回的都是新字符串,所以我们使用String的方法操作字符串后不影响原来的字符串;

StringBuffer 和 StringBuilder 返回的都是this,也就是对象本身,所有我们可以在代码中连着写append(xx).append(xxx).append(xxx);

不同的是StringBuffer的方法就加了synchronized 也就是我们说的线程安全。

总结一下:

a1bc8d60fb9cb95da0b715504a8f3834.png

String StringBuilder StringBuffer效率(性能)测试

我们通过各自拼接10000字符串来比较一下三者在执行时对时间和对内存资源的占用。

下面是测试代码:

package com.xzlf.string;

public class TestString {

public static void main(String[] args) {

// 使用 String 进行字符拼接

String str = "";

long num1 = Runtime.getRuntime().freeMemory();// 获取系统剩余内存空间

long time1 = System.currentTimeMillis();

for (int i = 0; i < 10000; i++) {

str += i; // 相当于产生了5000个对象

}

long num2 = Runtime.getRuntime().freeMemory();

long time2 = System.currentTimeMillis();

System.out.println("String 占用了内存:" + (num1 - num2));

System.out.println("String 占用了时间:" + (time2 - time1));

// 使用 StringBuilder 进行字符串拼接

StringBuilder sb = new StringBuilder("");

long num3 = Runtime.getRuntime().freeMemory();

long time3 = System.currentTimeMillis();

for (int i = 0; i < 10000; i++) {

sb.append(i);

}

long num4 = Runtime.getRuntime().freeMemory();

long time4 = System.currentTimeMillis();

System.out.println("StringBuilder 占用了内存:" + (num3 - num4));

System.out.println("StringBuilder 占用了时间:" + (time4 - time3));

// 使用 StringBuilder 进行字符串拼接

StringBuffer sb2 = new StringBuffer("");

long num5 = Runtime.getRuntime().freeMemory();

long time5 = System.currentTimeMillis();

for (int i = 0; i < 10000; i++) {

sb2.append(i);

}

long num6 = Runtime.getRuntime().freeMemory();

long time6 = System.currentTimeMillis();

System.out.println("StringBuffer 占用了内存:" + (num5 - num6));

System.out.println("StringBuffer 占用了时间:" + (time6 - time5));

}

}

以上代码运行结果为:

8bc440ef787fb9c683695f2d1bacbe13.png

可以看到,String创建了大量无用对象,消耗了大量内存耗时上大概是StringBuffer 和 builder的100倍。

当然,我们只循环了10000次,StringBuilder的优势不是很明显,为了防止java 虚拟机 jvm 垃圾回收机制的干扰 我们我StringBuilder 和 StringBuffer 单独拿出来吧循环次数加到10万次、100万次和1000万次测试:

代码吧String部分注释掉,由于循环次数较多,jvm 在运行时会有垃圾回收,内存对比会不正确,也先注释:

package com.xzlf.string;

public class TestString {

public static void main(String[] args) {

// 使用 String 进行字符拼接

//String str = "";

//long num1 = Runtime.getRuntime().freeMemory();// 获取系统剩余内存空间

//long time1 = System.currentTimeMillis();

//for (int i = 0; i < 10000; i++) {

//str += i; // 相当于产生了5000个对象

//}

//long num2 = Runtime.getRuntime().freeMemory();

//long time2 = System.currentTimeMillis();

//System.out.println("String 占用了内存:" + (num1 - num2));

//System.out.println("String 占用了时间:" + (time2 - time1));

// 使用 StringBuilder 进行字符串拼接

StringBuilder sb = new StringBuilder("");

long num3 = Runtime.getRuntime().freeMemory();

long time3 = System.currentTimeMillis();

for (int i = 0; i < 10000000; i++) {

sb.append(i);

}

long num4 = Runtime.getRuntime().freeMemory();

long time4 = System.currentTimeMillis();

//System.out.println("StringBuilder 占用了内存:" + (num3 - num4));

System.out.println("StringBuilder 占用了时间:" + (time4 - time3));

// 使用 StringBuilder 进行字符串拼接

StringBuffer sb2 = new StringBuffer("");

long num5 = Runtime.getRuntime().freeMemory();

long time5 = System.currentTimeMillis();

for (int i = 0; i < 10000000; i++) {

sb2.append(i);

}

long num6 = Runtime.getRuntime().freeMemory();

long time6 = System.currentTimeMillis();

//System.out.println("StringBuffer 占用了内存:" + (num5 - num6));

System.out.println("StringBuffer 占用了时间:" + (time6 - time5));

}

}

我这边测试10万次结果为:

be565007270554d8395d93ef513ba476.png

100万次结果为:

f58ab5cd1a09540370dd850cf428412c.png

1000万次结果为:

dbb67ce7caa63716c56fd5be42325087.png

在数量太少的情况下,StringBuilder 在StringBuffer加锁的情况下,并没有体现出优势,反而StringBuffer 更胜一筹。

这种情况相信很多测试过的小伙伴也应该遇到过???

对于这种情况,其实也不难理解,append的操作本质还是操作char[] 数组,我们还是继续看源码,

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才在性能上展现出了它的优势

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值