string stringbuilder stringbuffer三者的区别

从JDK源码看,String、StringBuilder、StringBuffer都是存放在char[] 数组字符串。
简单看下三者的部分源码:
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);
}

比较明显的是:
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 也就是我们说的线程安全。
总结一下:

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)); }
}

以上代码运行结果为:

可以看到,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万次结果为:

100万次结果为:

1000万次结果为:

在数量太少的情况下,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才在性能上展现出了它的优势

java中String StringBuilder StringBuffer比较和效率(性能)测试的更多相关文章

  1. java中string stringbuilder stringbuffer 的区别

    1. String 类 String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且大量浪费有限的内存空间. String a = "a&qu ...

  2. java中String、StringBuffer、StringBuilder的区别

    java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题.现在总结一下,看看他们的不同与相同. 1.可变与不可 ...

  3. Java基础知识(JAVA中String、StringBuffer、StringBuilder类的区别)

    java中String.StringBuffer.StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题.现在总结一下,看看他们的不同与相同. 1.可变与不可 ...

  4. Java基础——java中String、StringBuffer、StringBuilder的区别

    (转自:http://www.cnblogs.com/xudong-bupt/p/3961159.html) java中String.StringBuffer.StringBuilder是编程中经常使 ...

  5. Java中String, StringBuilder和StringBuffer

    Java中常用来处理字符串的类有三个: String, StringBuffer和StringBuilder. 区别 三者都继承自CharSequence接口, 首先说明三者间主要区别 String字 ...

  6. Java中String、StringBuffer和StringBuilder之间的区别

    String在Java中是字符串常量 例如 String str = "abc"; str = str + 1; System.out.println(str); 结果将是abc1 ...

  7. Java中String,StringBuffer和StringBuilder的区别(转载)

    String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简 要的说, String 类型和 StringBuffer 类型的主要性 ...

  8. Java中String、StringBuffer、StringBuilder、StringTokenizer的区别

    Java语言中,有4个类可以对字符或字符串进行操作,它们是Character.String.StringBuffer.StringTokenizer,其中Character用于单个字符操作,Strin ...

  9. Java中String、StringBuffer、StringBuilder区别与理解

    一.先比较String.StringBuffer.StringBuilder变量的HashCode值 使用System.out.println(obj.hashcode())输出的时对象的哈希码, 而 ...

随机推荐

  1. Jmeter4.0之插件安装(三)

    使用Jmeter的实际过程中,需要使用到很多插件,比如json的插件,还有就是做websocket接口测试的时候需要下载websocket的插件 到https://jmeter-plugins.org ...

  2. coding++:java-Date日期转换工具类

    Code: package com.tree.ztree_demo.utils; import org.springframework.util.ObjectUtils; import java.te ...

  3. [codevs2597]团伙<并查集>

    题目描述 Description 1920年的芝加哥,出现了一群强盗.如果两个强盗遇上了,那么他们要么是朋友,要么是敌人.而且有一点是肯定的,就是: 我朋友的朋友是我的朋友: 我敌人的敌人也是我的朋友 ...

  4. Jedis连接外部Redis

    Jedis连接外部Redis 1.在服务器开放端口redis默认6379,如果有宝塔面板则还需要在宝塔放行6379端口 2.修改redis.conf 注释掉 绑定IP 127.0.0.1 # bind ...

  5. MATLAB 随机过程基本理论

    一.平稳随机过程 1.严平稳随机过程 clc clear n=0:1000; x=randn(1,1001); subplot(211),plot(n,x); xlabel('n');ylabel(' ...

  6. Sql练习201908200916

    表结构: 一条sql修改多条数据(Sql server),oracle,mysql请自行尝试: ; go 完成.

  7. 实验七 MySQL语言结构

    实验七 MySQL语言结构 一.  实验内容: 1. 常量的使用 2. 变量的使用 3. 运算符的使用 4. 系统函数的使用 二.  实验项目:员工管理数据库 用于企业管理的员工管理数据库,数据库名为 ...

  8. 摩尔投票算法( Boyer-Moore Voting Algorithm)

    一.Majority Element题目介绍:给定一个长度为n的数组的时候,找出其中的主元素,即该元素在数组中出现的次数大于n/2的取整.题目中已经假定所给的数组一定含有元素,且主元素一定存在.一下是 ...

  9. php数据库应用程序建议

    一.保持独立的读写连接 开始就创建两个数据库连接是一个好的方法,一个用于读取,一个用于写入,并且允许不同的数据库服务器连接他们.如果只有一个服务器,则将它们设置彼此相同. 当操作为INSERT, UP ...

  10. Python操作rabbitmq系列(三):多个接收端消费消息

    接着上一章.这一章,我们要将同一个消息发给多个客户端.这就是发布订阅模式.直接看代码: 发送端: import pikaimport sys connection = pika.BlockingCon ...