引言

都说 StringBuilder 在处理字符串拼接上效率要强于 String,但有时候我们的理解可能会存在一定的偏差。最近我在测试数据导入效率的时候就发现我以前对 StringBuilder 的部分理解是错误的。后来我通过实践测试 + 找原理 的方式搞清楚了这块的逻辑。现在将过程分享给大家

测试用例

我们的代码在循环中拼接字符串一般有两种情况

  • 第一种就是每次循环将对象中的几个字段拼接成一个新字段,再赋值给对象
  • 第二种操作是在循环外创建一个字符串对象,每次循环向该字符串拼接新的内容。循环结束后得到拼接好的字符串

对于这两种情况,我创建了两个对照组

第一组:

在每次 For 循环中拼接字符串,即拼即用、用完即毁。分别使用 String 和 StringBuilder 拼接

/*** 循环内 String 拼接字符串,一次循环后销毁*/
public static void useString(){for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {String str = str1 + i + str2 + i + str3 + i + str4 ;}
}/*** 循环内 使用 StringBuilder 拼接字符串,一次循环后销毁*/
public static void useStringBuilder(){for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {StringBuilder sb = new StringBuilder();String s = sb.append(str1).append(i).append(str2).append(i).append(str3).append(i).append(str4).toString();}
}

第二组:

多次 For 循环拼接一个字符串,循环结束后使用字符串,使用后由垃圾回收器回收。也是分别使用 String 和 StringBuilder 拼接

/*** 多次循环拼接成一个字符串 用 String*/
public static void useStringSpliceOneStr (){String str = "";for (int i = 0; i < CYCLE_NUM_LOWER; i++) {str += str1 + str2 + str3 + str4 + i;}
}/*** 多次循环拼接成一个字符串 用 StringBuilder*/
public static void useStringBuilderSpliceOneStr(){StringBuilder sb = new StringBuilder();for (int i = 0; i < CYCLE_NUM_LOWER; i++) {sb.append(str1).append(str2).append(str3).append(str4).append(i);}
}

为了保证测试质量,在每个测试项目进行前。线程休息 2s,之后空跑 5 次热身。最后执行 5 次求平均时间的方式计算时间

public static int executeSometime(int kind, int num) throws InterruptedException {Thread.sleep(2000);int sum = 0;for (int i = 0; i < num + 5; i++) {long begin = System.currentTimeMillis();switch (kind){case 1:useString();break;case 2:useStringBuilder();break;case 3:useStringSpliceOneStr();break;case 4:useStringBuilderSpliceOneStr();break;default:return 0;}long end = System.currentTimeMillis();if(i > 5){sum += (end - begin);}}return sum / num;
}

主方法

public class StringTest {public static final int CYCLE_NUM_BIGGER = 10_000_000;public static final int CYCLE_NUM_LOWER = 10_000;public static final String str1 = "张三";public static final String str2 = "李四";public static final String str3 = "王五";public static final String str4 = "赵六";public static void main(String[] args) throws InterruptedException {int time = 0;int num = 5;time = executeSometime(1, num);System.out.println("String拼接 "+ CYCLE_NUM_BIGGER +" 次," + num + "次平均时间:" + time + " ms");time = executeSometime(2, num);System.out.println("StringBuilder拼接 "+ CYCLE_NUM_BIGGER +" 次," + num + "次平均时间:" + time + " ms");time = executeSometime(3, num);System.out.println("String拼接单个字符串 "+ CYCLE_NUM_LOWER +" 次," + num + "次平均时间:" + time + " ms");time = executeSometime(4, num);System.out.println("StringBuilder拼接单个字符串 "+ CYCLE_NUM_LOWER +" 次," + num + "次平均时间:" + time + " ms");}
}

测试结果

测试结果如下

结果分析

第一组

10_000_000 次循环拼接,在循环内使用 String 和 StringBuilder 的效率是一样的!为什么呢?搜索公纵号:MarkerHub,关注回复[ vue ]获取前后端入门教程!

使用 javap -c StringTest.class 反编译查看两个方法编译后的文件:

可以发现 String 方法拼接字符串编译器优化后使用的就是 StringBuilder、因此用例 1 和用例 2 的效率是一样的。

第二组

第二组的结果就是大家喜闻乐见的了,由于 10_000_000 次循环 String 拼接实在太慢所以我采用了 10_000 次拼接来分析。

分析用例 3:虽然编译器会对 String 拼接做优化,但是它每次在循环内创建 StringBuilder 对象,在循环内销毁。下次循环他有创建。相比较用例 4 在循环外创建,多了 n 次 new 对象、销毁对象的操作、n - 1 次将 StringBuilder 转换成 String 的操作 。效率低也是理所应当了。

扩展

第一组的测试还有一种写法:

/*** 循环内 使用 StringBuilder 拼接字符串,一次循环后销毁*/
public static void useStringBuilderOut(){StringBuilder sb = new StringBuilder();for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {
//            sb.setLength(0);sb.delete(0, sb.length());String s = sb.append(str1).append(i).append(str2).append(i).append(str3).append(i).append(str4).toString();}
}

循环外创建 StringBuilder 每次循环开始的时候清空 StringBuilder 的内容然后拼接。这种写法无论使用 sb.setLength(0); 还是 sb.delete(0, sb.length()); 效率都比直接在循环内使用 String / StringBuilder 慢。奈何才疏学浅我一直想不明白为什么他慢。我猜测是 new 对象的速度比重置长度慢,于是这样测试了以下:

public static void createStringBuider() {for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {StringBuilder sb = new StringBuilder();}
}public static void cleanStringBuider() {StringBuilder sb = new StringBuilder();for (int i = 0; i < CYCLE_NUM_BIGGER; i++) {sb.delete(0, sb.length());}
}

但是结果是 cleanStringBuider 更快。让我摸不着头脑

如果有大神看到希望可以帮忙分析分析

结论

  • 编译器会将 String 拼接优化成使用 StringBuilder,但是还是有一些缺陷的。主要体现在循环内使用字符串拼接,编译器不会创建单个 StringBuilder 以复用
  • 对于多次循环内拼接一个字符串的需求:StringBuilder 很快,因为其避免了 n 次 new 对象、销毁对象的操作,n - 1 次将 StringBuilder 转换成 String 的操作
  • StringBuilder 拼接不适用于循环内每次拼接即用的操作方式。因为编译器优化后的 String 拼接也是使用 StringBuilder 两者的效率一样。后者写起来还方便...

作者;后青春期的 Keats http://cnblogs.com/keatsCoder/p/13212289.html

for循环优化_昨天还在for循环里写加号拼接字符串的那个同事,今天已经不在了相关推荐

  1. 小程序循环里做字符串拼接_昨天还在for循环里写加号拼接字符串的那个同事,今天已经不在了...

    引言 都说 StringBuilder 在处理字符串拼接上效率要强于 String,但有时候我们的理解可能会存在一定的偏差.最近我在测试数据导入效率的时候就发现我以前对 StringBuilder 的 ...

  2. 昨天还在 for 循环里写加号拼接字符串的那个同事,今天已经不在了

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源: 测试用例 测试结果 结果分析 第一组 第二组 扩展 结论 ...

  3. 昨天还在for循环里写加号拼接字符串的那个同事,今天已经不在了

    点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达 今日推荐:分享一套基于SpringBoot和Vue的企业级中后台开源项目,这个项目有点哇塞!个人原创100W +访问量博客: ...

  4. for循环优化_前端js if判断以及for循环该如何写的更好,优化upup

    大致介绍 这是小m马发布的第一篇文章,关于前端领域的开发习惯,这是自己的一些经验总结,希望可以给大家带来一定的帮助,提升自己的开发效率以及代码质量,m马会用react,以及vue分别举出一些正反例来进 ...

  5. python 引用计数 循环引用_引用计数无法解决循环引用,CPython为何还使用它?

    取消excel表中的循环引用的步2113骤如下:1.打开一个5261Excel文件.打开之后,4102在操作表中的单元格时,1653出现了循环引用警告.2.点击左上角的 文件 菜单,在出现的菜单中点击 ...

  6. java迭代器和for循环区别_迭代器和增强for循环

    Iterator迭代器的使用: 迭代:Iterator,即collection集合元素的通用获取方式 java.util.Iterator接口.迭代器(对集合进行遍历) 有两个常用的方法 Boolea ...

  7. python的for循环语句_干货丨Python的循环语句基础讲解!

    我们知道计算机程序语言一般是按照顺序执行的,那么编程语言就提供了各种控制结构,允许更复杂的执行路径,其中循环语句的作用就是允许我们执行一个语句或语句组多次. 在Python中循环语句的类型主要有以下几 ...

  8. java 循环依赖_浅谈Spring解决循环依赖的三种方式

    引言:循环依赖就是N个类中循环嵌套引用,如果在日常开发中我们用new 对象的方式发生这种循环依赖的话程序会在运行时一直循环调用,直至内存溢出报错.下面说一下Spring是如果解决循环依赖的. 第一种: ...

  9. 多线程 循环 锁_大多数人还不清楚的iOS多线程

    你不知道的的 iOS 多线程 程序员用有限的生命去追求无限的知识. 有言在先 首先我不是故意要做标题党的,也不是我要炒冷饭,我只是想换个姿势看多线程,本文大部分内容在分析如何造死锁,奈何功力尚浅,然而 ...

最新文章

  1. 郭为:大数据时代的企业管理挑战
  2. java中String对象和String变量
  3. 【多图】近距离接触甲骨文总裁马克赫德,Oracle在上海香格里拉酒店数据中心优化专题研讨会...
  4. canal原理的一些学习-1(canal的一些原理性介绍)
  5. sqlite mysql pgsql_比较MySQL,PostgreSQL和SQLite中的数据库列类型?(跨图)
  6. 谈谈分布式事务(Distributed Transaction)[共5篇]
  7. 几个接下来要解决的问题
  8. can test 接收报文_获取CAN报文并解析
  9. 在Salesforce中以PDF的格式显示对应的页面
  10. 互联网控制协议ICMP
  11. “英伦配”收视牛过本山,网管软件如何配奇兵
  12. python基础--面向对象之封装
  13. vant ui的dialog二次封装使用
  14. DSP篇--C6701的COFF文件有效数据分析与提取
  15. vuefilters过滤器的使用,给金钱价格加上符号单位
  16. 汽车营销与保险【3】
  17. Openwrt 18.06 iPhone XR usb tethering导致内核崩溃问题解决方案
  18. CloudComparePCL 点云点匹配(基于点到面的距离)
  19. 2017-12-16 机器视觉表面缺陷检测综述
  20. 我们都没有迎来决赛---Leo读《不是孙振耀写的职场感言》(1)

热门文章

  1. mysql8.0.13可以用在生产环境_(13)生产环境出现大量DB死锁
  2. linux中vi模式中c命令,“Linux”系统中“vi ^C ”命令是什么意思?
  3. 如何打开mysql的批处理宫娥能_如何在MySQL中进行批处理插入
  4. POJ 2823 Sliding Window (单调队列)
  5. Python+Selenium学习笔记5 - python官网的tutorial - 交互模式下的操作
  6. 4.2.#{}和${}的用法
  7. HDU 2643 Rank:第二类Stirling数
  8. 阿里云API网关(18)请求报文和响应报文
  9. 微信开发博客——柳峰
  10. Transformer 是万能的吗?