一、字符串连接的效率问题

使用String连接字符串时为什么慢?

小知识点

java中对数组进行初始化后,该数组所占的内存空间、数组长度都是不可变的。

创建一个字符串,为字符串对象分配内存空间,会耗费掉一定的时间(CPU)与空间(内存)代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能。

过多无用的中间对象

每次连接字符串时都会创建一个新的String对象,随着拼接次数的增多,这个对象会越来越大。 如,进行100次拼接需要创建100个String对象才能够达到目的。

StringBuilder在连接时为什么效率更高?

字符数组的扩容机制:

  1. private void ensureCapacityInternal(int minimumCapacity) {
  2. // 最小所需容量minimumCapacity是否比原数组长度要长
  3. // overflow-conscious code
  4. if (minimumCapacity - value.length > 0) {
  5. value = Arrays.copyOf(value,
  6. newCapacity(minimumCapacity));
  7. }
  8. }
  9.  
  10. private int newCapacity(int minCapacity) {
  11. // 计算扩容之后的容量newCapacity
  12. // overflow-conscious code
  13. int newCapacity = (value.length << 1) + 2;
  14. // 扩容后还小于所需的最小容量
  15. if (newCapacity - minCapacity < 0) {
  16. // 设置新容量为最小所需容量minimumCapacity
  17. newCapacity = minCapacity;
  18. }
  19. // newCapacity是否溢出,newCapacity是否比数组所能分配的最大容量 MAX_ARRAY_SIZE 还要大。
  20. return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
  21. ? hugeCapacity(minCapacity)
  22. : newCapacity;
  23. }
  24.  
  25. private int hugeCapacity(int minCapacity) {
  26. // 最小所需容量minCapacity大于Integer.MAX_VALUE时抛出内存溢出异常
  27. if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
  28. throw new OutOfMemoryError();
  29. }
  30. // 如果minCapacity介于MAX_ARRAY_SIZE和Integer.MAX_VALUE之间,则新的容量为minCapacity,否则直接使用MAX_ARRAY_SIZE作为新的容量。
  31. return (minCapacity > MAX_ARRAY_SIZE)
  32. ? minCapacity : MAX_ARRAY_SIZE;
  33. }

向原StringBuilder对象中追加字符串时:

1.追加对象str为null时追加'null'字符

2.确认是否需要进行扩容操作

  • 最小所需容量minimumCapacity是否比原数组长度要长,即当原数组长度不能满足所需最小容量时进行扩容操作。
  • 计算扩容之后的容量newCapacity,newCapacity = (value.length * 2) + 2。
  • 扩容后是否还小于所需的最小容量,如果小于则直接设置新容量为最小所需容量minimumCapacity。
  • newCapacity是否溢出,newCapacity是否比数组所能分配的最大容量 MAX_ARRAY_SIZE 还要大。如果是的话则判断,最小所需容量minCapacity大于Integer.MAX_VALUE时抛出内存溢出异常,如果minCapacity介于MAX_ARRAY_SIZE和Integer.MAX_VALUE之间,则新的容量为minCapacity,否则直接使用MAX_ARRAY_SIZE作为新的容量。

3.str.getChars()将str追加到value的末尾

效率高的原因

  1. 扩容机制保证了,只有在满足扩容条件 minimumCapacity - value.length > 0 时才会进行扩容生成新的数组,所以大部分情况都是在对原数组进行操作,避免了产生过多的无用char[]对象,节省了系统资源的开销。

代码

  1. /**
  2. * 比较字符串连接速度
  3. *
  4. * @Author: lingyejun
  5. * @Date: 2019/8/17
  6. * @Describe:
  7. * @Modified By:
  8. */
  9. public class LinkCompare {
  10.  
  11. /**
  12. * 原始字符串连接
  13. *
  14. * @param times
  15. */
  16. public static void linkByString(int times) {
  17.  
  18. Long startTime = System.currentTimeMillis();
  19.  
  20. String initStr = "";
  21. for (int i = 0; i < times; i++) {
  22. initStr = initStr + i;
  23. }
  24.  
  25. Long endTime = System.currentTimeMillis();
  26.  
  27. System.out.println("String 连接 " + times + " 次 消耗:" + (endTime - startTime) + "ms");
  28. }
  29.  
  30. /**
  31. * 使用StringBuilder连接字符串
  32. *
  33. * @param times
  34. */
  35. public static void linkByStringBuilder(int times) {
  36.  
  37. Long startTime = System.currentTimeMillis();
  38.  
  39. StringBuilder initStr = new StringBuilder();
  40. for (int i = 0; i < times; i++) {
  41. initStr.append(i);
  42. }
  43.  
  44. Long endTime = System.currentTimeMillis();
  45.  
  46. System.out.println("StringBuilder 连接 " + times + " 次 消耗:" + (endTime - startTime) + "ms");
  47. }
  48.  
  49. /**
  50. * 使用StringBuffer连接字符串
  51. *
  52. * @param times
  53. */
  54. public static void linkByStringBuffer(int times) {
  55.  
  56. Long startTime = System.currentTimeMillis();
  57.  
  58. StringBuffer initStr = new StringBuffer();
  59. for (int i = 0; i < times; i++) {
  60. initStr.append(i);
  61. }
  62.  
  63. Long endTime = System.currentTimeMillis();
  64.  
  65. System.out.println("StringBuffer 连接 " + times + " 次 消耗:" + (endTime - startTime) + "ms");
  66. }
  67.  
  68. public static void main(String[] args) {
  69.  
  70. // 100000000
  71. linkByStringBuilder(40000);
  72. //-XX:+PrintGCDetails
  73. //linkByString(40000);
  74.  
  75. }
  76. }

二、StringBuilder和String Buffer的线程安全比较

验证StringBuffer的线程安全性

线程不安全的原因

  1. public StringBuilder append(String str) {
  2. super.append(str);
  3. return this;
  4. }
  5.  
  6. public synchronized StringBuffer append(String str) {
  7. toStringCache = null;
  8. super.append(str);
  9. return this;
  10. }

测试代码

  1. import java.util.ArrayList;
  2. import java.util.List;
  3.  
  4. /**
  5. * StringBuilder和StringBuffer的并发测验
  6. *
  7. * @Author: lingyejun
  8. * @Date: 2019/8/17
  9. * @Describe:
  10. * @Modified By:
  11. */
  12. public class SecurityCompare {
  13.  
  14. public void stringBuilderTest() {
  15.  
  16. // 初始化StringBuilder
  17. StringBuilder stringBuilder = new StringBuilder();
  18.  
  19. // joinList
  20. List<StringBuilderThread> joinList = new ArrayList<>();
  21.  
  22. // 模拟并发场景
  23. for (int i = 0; i < 1000; i++) {
  24. StringBuilderThread sbt = new StringBuilderThread(stringBuilder);
  25. sbt.start();
  26. joinList.add(sbt);
  27. }
  28.  
  29. // 等待append线程执行完毕后再执行主线程
  30. for (StringBuilderThread thread : joinList) {
  31. try {
  32. thread.join();
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37.  
  38. // 打印最终的结果
  39. System.out.println("StringBuilder 并发append的结果: " + stringBuilder.length());
  40. }
  41.  
  42. public void stringBufferTest() {
  43.  
  44. // 初始化StringBuffer
  45. StringBuffer stringBuffer = new StringBuffer();
  46.  
  47. // joinList
  48. List<StringBufferThread> joinList = new ArrayList<>();
  49.  
  50. // 模拟并发场景
  51. for (int i = 0; i < 1000; i++) {
  52. StringBufferThread sbf = new StringBufferThread(stringBuffer);
  53. sbf.start();
  54. joinList.add(sbf);
  55. }
  56.  
  57. // 等待append线程执行完毕后再执行主线程
  58. for (StringBufferThread thread : joinList) {
  59. try {
  60. thread.join();
  61. } catch (InterruptedException e) {
  62. e.printStackTrace();
  63. }
  64. }
  65.  
  66. // 打印最终的结果
  67. System.out.println("StringBuffer 并发append的结果: " + stringBuffer.length());
  68. }
  69.  
  70. public static void main(String[] args) {
  71.  
  72. SecurityCompare securityCompare = new SecurityCompare();
  73.  
  74. securityCompare.stringBuilderTest();
  75. securityCompare.stringBufferTest();
  76.  
  77. }
  78.  
  79. public static class StringBuilderThread extends Thread {
  80.  
  81. private StringBuilder stringBuilder;
  82.  
  83. public StringBuilderThread(StringBuilder stringBuilder) {
  84. this.stringBuilder = stringBuilder;
  85. }
  86.  
  87. @Override
  88. public void run() {
  89. try {
  90. Thread.sleep(10);
  91. } catch (InterruptedException e) {
  92. e.printStackTrace();
  93. }
  94. stringBuilder.append("a");
  95. try {
  96. Thread.sleep(10);
  97. } catch (InterruptedException e) {
  98. e.printStackTrace();
  99. }
  100.  
  101. }
  102. }
  103.  
  104. private static class StringBufferThread extends Thread {
  105.  
  106. private StringBuffer stringBuffer;
  107.  
  108. public StringBufferThread(StringBuffer stringBuffer) {
  109. this.stringBuffer = stringBuffer;
  110. }
  111.  
  112. @Override
  113. public void run() {
  114. try {
  115. Thread.sleep(10);
  116. } catch (InterruptedException e) {
  117. e.printStackTrace();
  118. }
  119. stringBuffer.append("a");
  120. try {
  121. Thread.sleep(10);
  122. } catch (InterruptedException e) {
  123. e.printStackTrace();
  124. }
  125.  
  126. }
  127. }
  128. }

三、结论

  1. String为固定长度的字符串,StringBuilder和StringBuffer为变长字符串。
  2. StringBuffer是线程安全的,StringBuilder是非线程安全的。
  3. StringBuilder和StringBuffer的默认初始容量是16,可以提前预估好字符串的长度,进一步减少扩容带来的额外开销。

手把手实例对比String、StringBuilder字符串的连接效率及StringBuilder和StringBuffer线程安全的比较的更多相关文章

  1. 6.2 C++ string类型字符串的连接

    参考:http://www.weixueyuan.net/view/6391.html 总结: 对于string类型变量,我们可以直接用“+”或者“+=”进行字符串的连接,操作符非常方便. 用“+”风 ...

  2. 从String类型字符串的比较到StringBuffer和StringBuilder

    1. String类型 String类源码 为了从本质上理解String类型的特性所在,我们从String类型的源码看起,在源码中String类的注释中存在以下: /**Strings are con ...

  3. Java基础 -- 连接字符串时,使用+还是StringBuilder

    结论 1-源代码中使用的+连接,实际上都使用的是StringBuilder. 2-用jad工具反编译,好处之一就是可以同时生成字节码和源代码.这样可以进行对照研究. ----------------- ...

  4. StringBuilder String string.Concat 字符串拼接速度再议

    首先看测试代码: public class StringSpeedTest { "; public string StringAdd(int count) { string str = st ...

  5. StringBuilder String string.Concat 字符串拼接速度

    首先看测试代码: public class StringSpeedTest { "; public string StringAdd(int count) { string str = st ...

  6. .NET面试题解析(03)-string与字符串操作

      系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 字符串可以说是C#开发中最常用的类型了,也是对系统性能影响很关键的类型,熟练掌握字符串的操作非常重要. 常 ...

  7. String.format字符串拼接

    一.String.Format1.简介      String类的format()方法用于创建格式化的字符串以及连接多个字符串对象. 2.参数      format()方法有两种重载形式. form ...

  8. C# 数据类型之 String(字符串)

    Ø  简介 在开发中最常见的数据类型就是 String 类型,即字符串类型.为什么要单独讨论下这个类型,是因为:它是系统内置的基础数据类型:它的使用频率非常高:它是一个特殊的引用类型.其实大家都会使用 ...

  9. String拼接字符串效率低,你知道原因吗?

    面试官Q1:请问为什么String用"+"拼接字符串效率低下,最好能从JVM角度谈谈吗? 对于这个问题,我们先来看看如下代码: public class StringTest { ...

随机推荐

  1. Java visualvm

    简介 VisualVM是一个集成多个JDK命令行工具的可视化工具.可以作为Java应用程序性能分析和运行监控的工具.开发人员可以利用它来监控.分 析线程信息,浏览内存堆数据.系统管理员可以利用它来监测 ...

  2. iOS之集成GoogleMap定位、搜索注意事项

    简介: 最近花了些时间看了GoogleMap官方文件并集成到国际版app中,网上关于GoogleMap for iOS的讲解相对Android来说少一点,比较有帮助的几乎全是英文文档.下面是我开发过程 ...

  3. 【开发工具】- Myeclipse10.7破解方法

    1.下载myeclipse 10,如果没有,可以使用链接:https://pan.baidu.com/s/1l9juqD4ALMuepVL6e5kgjA 密码:kpx6:当然时间久了可能链接失效,如有 ...

  4. 对于flex布局的使用心得

    弹性盒子flex: 对于客户端的布局非常有用,不管是平均分配space-around这个属性还是两端对齐space-betwee在页面布局的时候都会有很好的表现. 对于部分内容区域中,具有很多大致内容 ...

  5. Html-元素类型笔记

    注意点: 元素类型分为 块级元素 和 行内元素 块级元素: 在网页中以块的形式显示,默认情况都会占据一行,两个相邻的块级元素不会出现并列显示的元素,按照顺序自上而下排列. 块级元素可以定义自己的宽度和 ...

  6. pychrom注册码

    http://angame.top/wx/web/zhucema/ YZVR7WDLV8-eyJsaWNlbnNlSWQiOiJZWlZSN1dETFY4IiwibGljZW5zZWVOYW1lIjo ...

  7. 如何传递大文件(GB级别)

    一.拆分:压缩工具,压缩并拆分为多个小文件. 二.QQ离线传输 QQ离线文件有限制条件: 1.离线传送的文件,为用户保存7天,逾期接收方不接收文件,系统将自动删除该文件: 2. 离线传送的文件,单个文 ...

  8. Linux操作系统的进程管理

    Linux操作系统的进程管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程相关概念 1>.进程概述 内核的功用: 进程管理.文件系统.网络功能.内存管理.驱动程序. ...

  9. 完美快速解决百度分享不支持HTTPS的问题

    百度分享不支持HTTPS这件事由来已久,我之前向百度分享提交过这个问题,无果!但近期themebetter主题用户咨询的比较多,我们就总结了解决方案. 第一步:下载百度分享必备文件 点此下载stati ...

  10. [AI] 切换cuda版本的万金油

    1. 环境 ubuntu16.04 GTX1080Ti x 4 nvidia-418 cuda-10.1 pytorch1.0.0 目标:在最新的显卡驱动下,使用不同版本的cuda和深度学习框架来执行 ...