string stringbuilder stringbuffer三者的区别

从JDK源码看,String、StringBuilder、StringBuffer都是存放在char[] 数组字符串。
简单看下三者的部分源码:
String定义属性和构造方法:

  1. public final class String
  2. implements java.io.Serializable, Comparable<String>, CharSequence {
  3. private final char value[];
  4. public String() {
  5. this.value = "".value;
  6. }
  7. public String(String original) {
  8. this.value = original.value;
  9. this.hash = original.hash;
  10. }
  11. public String(char value[]) {
  12. this.value = Arrays.copyOf(value, value.length);
  13. }

StringBuilder源码:

  1. public final class StringBuilder
  2. extends AbstractStringBuilder
  3. implements java.io.Serializable, CharSequence
  4. {
  5. public StringBuilder() {
  6. super(16);
  7. }
  8. public StringBuilder(int capacity) {
  9. super(capacity);
  10. }
  11. public StringBuilder(String str) {
  12. super(str.length() + 16);
  13. append(str);
  14. }

StringBuffer源码:

  1. public final class StringBuffer
  2. extends AbstractStringBuilder
  3. implements java.io.Serializable, CharSequence
  4. {
  5. private transient char[] toStringCache;
  6. public StringBuffer() {
  7. super(16);
  8. }

比较明显的是:
String 中定义的char[] 数组是用final 修饰,所以,String 是不可变字符序列,而StringBuilder和StringBuffer是可变字符序列;
如果Sting 需要改变则需要重新创建新对象;
StringBuffer 和 StringBuilder 都继承 AbstractStringBuilder类,他们在初始化时,都是调用父类的构造器。

接下来,我们在简单看下AbstractStringBuilder类源码:

  1. abstract class AbstractStringBuilder implements Appendable, CharSequence {
  2. /**
  3. * The value is used for character storage.
  4. */
  5. char[] value;
  6. /**
  7. * The count is the number of characters used.
  8. */
  9. int count;
  10. /**
  11. * This no-arg constructor is necessary for serialization of subclasses.
  12. */
  13. AbstractStringBuilder() {
  14. }
  15. /**
  16. * Creates an AbstractStringBuilder of the specified capacity.
  17. */
  18. AbstractStringBuilder(int capacity) {
  19. value = new char[capacity];
  20. }

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

  1. private int newCapacity(int minCapacity) {
  2. // overflow-conscious code
  3. int newCapacity = (value.length << 1) + 2;
  4. if (newCapacity - minCapacity < 0) {
  5. newCapacity = minCapacity;
  6. }
  7. return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
  8. ? hugeCapacity(minCapacity)
  9. : newCapacity;
  10. }
  11. private int hugeCapacity(int minCapacity) {
  12. if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
  13. throw new OutOfMemoryError();
  14. }
  15. return (minCapacity > MAX_ARRAY_SIZE)
  16. ? minCapacity : MAX_ARRAY_SIZE;
  17. }

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

  1. public String substring(int beginIndex) {
  2. if (beginIndex < 0) {
  3. throw new StringIndexOutOfBoundsException(beginIndex);
  4. }
  5. int subLen = value.length - beginIndex;
  6. if (subLen < 0) {
  7. throw new StringIndexOutOfBoundsException(subLen);
  8. }
  9. return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
  10. }
  11. public String substring(int beginIndex, int endIndex) {
  12. if (beginIndex < 0) {
  13. throw new StringIndexOutOfBoundsException(beginIndex);
  14. }
  15. if (endIndex > value.length) {
  16. throw new StringIndexOutOfBoundsException(endIndex);
  17. }
  18. int subLen = endIndex - beginIndex;
  19. if (subLen < 0) {
  20. throw new StringIndexOutOfBoundsException(subLen);
  21. }
  22. return ((beginIndex == 0) && (endIndex == value.length)) ? this
  23. : new String(value, beginIndex, subLen);
  24. }

StringBuilder的常用方法:

  1. @Override
  2. public StringBuilder append(int i) {
  3. super.append(i);
  4. return this;
  5. }
  6. @Override
  7. public StringBuilder append(long lng) {
  8. super.append(lng);
  9. return this;
  10. }
  11. @Override
  12. public StringBuilder append(float f) {
  13. super.append(f);
  14. return this;
  15. }

StringBuffer的常用方法:

  1. @Override
  2. public synchronized StringBuffer append(CharSequence s, int start, int end)
  3. {
  4. toStringCache = null;
  5. super.append(s, start, end);
  6. return this;
  7. }
  8. @Override
  9. public synchronized StringBuffer append(char[] str) {
  10. toStringCache = null;
  11. super.append(str);
  12. return this;
  13. }

从它们的常用方法可以看出:
String 每次返回的都是新字符串,所以我们使用String的方法操作字符串后不影响原来的字符串;
StringBuffer 和 StringBuilder 返回的都是this,也就是对象本身,所有我们可以在代码中连着写append(xx).append(xxx).append(xxx);
不同的是StringBuffer的方法就加了synchronized 也就是我们说的线程安全。
总结一下:

String StringBuilder StringBuffer效率(性能)测试

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

  1. package com.xzlf.string;
  2. public class TestString {
  3. public static void main(String[] args) {
  4. // 使用 String 进行字符拼接
  5. String str = "";
  6. long num1 = Runtime.getRuntime().freeMemory();// 获取系统剩余内存空间
  7. long time1 = System.currentTimeMillis();
  8. for (int i = 0; i < 10000; i++) {
  9. str += i; // 相当于产生了5000个对象
  10. }
  11. long num2 = Runtime.getRuntime().freeMemory();
  12. long time2 = System.currentTimeMillis();
  13. System.out.println("String 占用了内存:" + (num1 - num2));
  14. System.out.println("String 占用了时间:" + (time2 - time1));
  15. // 使用 StringBuilder 进行字符串拼接
  16. StringBuilder sb = new StringBuilder("");
  17. long num3 = Runtime.getRuntime().freeMemory();
  18. long time3 = System.currentTimeMillis();
  19. for (int i = 0; i < 10000; i++) {
  20. sb.append(i);
  21. }
  22. long num4 = Runtime.getRuntime().freeMemory();
  23. long time4 = System.currentTimeMillis();
  24. System.out.println("StringBuilder 占用了内存:" + (num3 - num4));
  25. System.out.println("StringBuilder 占用了时间:" + (time4 - time3));
  26. // 使用 StringBuilder 进行字符串拼接
  27. StringBuffer sb2 = new StringBuffer("");
  28. long num5 = Runtime.getRuntime().freeMemory();
  29. long time5 = System.currentTimeMillis();
  30. for (int i = 0; i < 10000; i++) {
  31. sb2.append(i);
  32. }
  33. long num6 = Runtime.getRuntime().freeMemory();
  34. long time6 = System.currentTimeMillis();
  35. System.out.println("StringBuffer 占用了内存:" + (num5 - num6));
  36. System.out.println("StringBuffer 占用了时间:" + (time6 - time5));
  37. }
  38. }

以上代码运行结果为:

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

当然,我们只循环了10000次,StringBuilder的优势不是很明显,为了防止java 虚拟机 jvm 垃圾回收机制的干扰 我们我StringBuilder 和 StringBuffer 单独拿出来吧循环次数加到10万次、100万次和1000万次测试:
代码吧String部分注释掉,由于循环次数较多,jvm 在运行时会有垃圾回收,内存对比会不正确,也先注释:

  1. package com.xzlf.string;
  2. public class TestString {
  3. public static void main(String[] args) {
  4. // 使用 String 进行字符拼接
  5. // String str = "";
  6. // long num1 = Runtime.getRuntime().freeMemory();// 获取系统剩余内存空间
  7. // long time1 = System.currentTimeMillis();
  8. // for (int i = 0; i < 10000; i++) {
  9. // str += i; // 相当于产生了5000个对象
  10. // }
  11. // long num2 = Runtime.getRuntime().freeMemory();
  12. // long time2 = System.currentTimeMillis();
  13. // System.out.println("String 占用了内存:" + (num1 - num2));
  14. // System.out.println("String 占用了时间:" + (time2 - time1));
  15. // 使用 StringBuilder 进行字符串拼接
  16. StringBuilder sb = new StringBuilder("");
  17. long num3 = Runtime.getRuntime().freeMemory();
  18. long time3 = System.currentTimeMillis();
  19. for (int i = 0; i < 10000000; i++) {
  20. sb.append(i);
  21. }
  22. long num4 = Runtime.getRuntime().freeMemory();
  23. long time4 = System.currentTimeMillis();
  24. // System.out.println("StringBuilder 占用了内存:" + (num3 - num4));
  25. System.out.println("StringBuilder 占用了时间:" + (time4 - time3));
  26. // 使用 StringBuilder 进行字符串拼接
  27. StringBuffer sb2 = new StringBuffer("");
  28. long num5 = Runtime.getRuntime().freeMemory();
  29. long time5 = System.currentTimeMillis();
  30. for (int i = 0; i < 10000000; i++) {
  31. sb2.append(i);
  32. }
  33. long num6 = Runtime.getRuntime().freeMemory();
  34. long time6 = System.currentTimeMillis();
  35. // System.out.println("StringBuffer 占用了内存:" + (num5 - num6));
  36. System.out.println("StringBuffer 占用了时间:" + (time6 - time5));
  37. }
  38. }

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

100万次结果为:

1000万次结果为:

在数量太少的情况下,StringBuilder 在StringBuffer加锁的情况下,并没有体现出优势,反而StringBuffer 更胜一筹。
这种情况相信很多测试过的小伙伴也应该遇到过???

对于这种情况,其实也不难理解,append的操作本质还是操作char[] 数组,我们还是继续看源码,
StringBuffer比StringBuilder多了一个缓冲区,
我们看下StringBuffer的toString方法:

  1. @Override
  2. public synchronized String toString() {
  3. if (toStringCache == null) {
  4. toStringCache = Arrays.copyOfRange(value, 0, count);
  5. }
  6. return new String(toStringCache, true);
  7. }

StringBuilder 的toString()方法:

  1. @Override
  2. public String toString() {
  3. // Create a copy, don't share the array
  4. return new String(value, 0, count);
  5. }

我们可以看到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. SpringCloud-Nacos/OpenFien/Gateway的基本介绍及快速上手

    一.Spring-Cloud-Alibaba-Nacos 注册中心 1.下载.安装 Nacos 下载地址:https://github.com/alibaba/nacos/releases 下载后解压 ...

  2. ArcGIS中影像图去黑边

    通常情况下有些影像图的背景会显示黑色,所以需要将影像图的格式转换一下,将黑色背景转换为透明色.具体去除河边的步骤如下: 1.在catalog中,选中要转换的影像图: 2.右键——导出——将栅格导出为不 ...

  3. Python python对象 enumerate

    """ enumerate(iterable[, start]) -> iterator for index, value of iterable Return a ...

  4. 蓝桥杯 K好数(Java)

    越来越觉得自己菜,一道简单的动态规划写不出来,题解也是看了很多份才看懂了,所以尽量以图表的方式写了题解,希望我的题解能帮到其他人吧.(;´Д`) 首先是题目: 输入描述: 输入包含两个正整数,K和L. ...

  5. Python批量修改文件名模板

    源码如下:import os import re import sys filePath = r'F:\BaiduNetdiskDownload\COVID-19CTSeg\3DUNet-Pytorc ...

  6. html ajax 异步加载 局部刷新

    1. getJSON 优点: 简单高效,直接返回处理好的json数据 缺点: 只能使用get请求和使用json数据 <script src ='jquery.min.js'></sc ...

  7. [ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?

    昨天有个朋友在微信上问我一个问题:他希望通过动态脚本的形式实现对ASP.NET Core MVC应用的扩展,比如在程序运行过程中上传一段C#脚本将其中定义的Controller类型注册到应用中,问我是 ...

  8. Win 10 C 盘突然爆满,怎么清理?

    Win 10 C 盘突然爆满,怎么清理? 使用windows的小伙伴们都知道,C盘是安装系统的,有时候不知道为啥突然就爆满了,查看我的电脑,C盘显示红色的.是不是感觉狠揪心,想删除一些东西有不敢删除, ...

  9. 【Linux】Linux(一)Linux常用命令

    一 命令行提示符 1.[root@localhost ~]# 当前登录用户@主机名:当前所在目录$ # 超级用户 $  普通用户 当前所在目录:~ 用户家目录 管理员 /root 普通用户 /home ...

  10. Java第十四天,集合、迭代器的使用

    集合 集合框架 一.Collection 1.定义方法: Collection<E> obj = new Collection子类<>(); 因为Collection是一个抽象 ...