手把手实例对比String、StringBuilder字符串的连接效率及StringBuilder和StringBuffer线程安全的比较
一、字符串连接的效率问题
使用String连接字符串时为什么慢?
小知识点
java中对数组进行初始化后,该数组所占的内存空间、数组长度都是不可变的。
创建一个字符串,为字符串对象分配内存空间,会耗费掉一定的时间(CPU)与空间(内存)代价,作为最基础的数据类型,大量频繁的创建字符串,极大程度地影响程序的性能。
过多无用的中间对象
每次连接字符串时都会创建一个新的String对象,随着拼接次数的增多,这个对象会越来越大。 如,进行100次拼接需要创建100个String对象才能够达到目的。
StringBuilder在连接时为什么效率更高?
字符数组的扩容机制:
- private void ensureCapacityInternal(int minimumCapacity) {
- // 最小所需容量minimumCapacity是否比原数组长度要长
- // overflow-conscious code
- if (minimumCapacity - value.length > 0) {
- value = Arrays.copyOf(value,
- newCapacity(minimumCapacity));
- }
- }
- private int newCapacity(int minCapacity) {
- // 计算扩容之后的容量newCapacity
- // overflow-conscious code
- int newCapacity = (value.length << 1) + 2;
- // 扩容后还小于所需的最小容量
- if (newCapacity - minCapacity < 0) {
- // 设置新容量为最小所需容量minimumCapacity
- newCapacity = minCapacity;
- }
- // newCapacity是否溢出,newCapacity是否比数组所能分配的最大容量 MAX_ARRAY_SIZE 还要大。
- return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
- ? hugeCapacity(minCapacity)
- : newCapacity;
- }
- private int hugeCapacity(int minCapacity) {
- // 最小所需容量minCapacity大于Integer.MAX_VALUE时抛出内存溢出异常
- if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
- throw new OutOfMemoryError();
- }
- // 如果minCapacity介于MAX_ARRAY_SIZE和Integer.MAX_VALUE之间,则新的容量为minCapacity,否则直接使用MAX_ARRAY_SIZE作为新的容量。
- return (minCapacity > MAX_ARRAY_SIZE)
- ? minCapacity : MAX_ARRAY_SIZE;
- }
向原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的末尾
效率高的原因
- 扩容机制保证了,只有在满足扩容条件
minimumCapacity - value.length > 0
时才会进行扩容生成新的数组,所以大部分情况都是在对原数组进行操作,避免了产生过多的无用char[]对象,节省了系统资源的开销。
代码
- /**
- * 比较字符串连接速度
- *
- * @Author: lingyejun
- * @Date: 2019/8/17
- * @Describe:
- * @Modified By:
- */
- public class LinkCompare {
- /**
- * 原始字符串连接
- *
- * @param times
- */
- public static void linkByString(int times) {
- Long startTime = System.currentTimeMillis();
- String initStr = "";
- for (int i = 0; i < times; i++) {
- initStr = initStr + i;
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("String 连接 " + times + " 次 消耗:" + (endTime - startTime) + "ms");
- }
- /**
- * 使用StringBuilder连接字符串
- *
- * @param times
- */
- public static void linkByStringBuilder(int times) {
- Long startTime = System.currentTimeMillis();
- StringBuilder initStr = new StringBuilder();
- for (int i = 0; i < times; i++) {
- initStr.append(i);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("StringBuilder 连接 " + times + " 次 消耗:" + (endTime - startTime) + "ms");
- }
- /**
- * 使用StringBuffer连接字符串
- *
- * @param times
- */
- public static void linkByStringBuffer(int times) {
- Long startTime = System.currentTimeMillis();
- StringBuffer initStr = new StringBuffer();
- for (int i = 0; i < times; i++) {
- initStr.append(i);
- }
- Long endTime = System.currentTimeMillis();
- System.out.println("StringBuffer 连接 " + times + " 次 消耗:" + (endTime - startTime) + "ms");
- }
- public static void main(String[] args) {
- // 100000000
- linkByStringBuilder(40000);
- //-XX:+PrintGCDetails
- //linkByString(40000);
- }
- }
二、StringBuilder和String Buffer的线程安全比较
验证StringBuffer的线程安全性
线程不安全的原因
- public StringBuilder append(String str) {
- super.append(str);
- return this;
- }
- public synchronized StringBuffer append(String str) {
- toStringCache = null;
- super.append(str);
- return this;
- }
测试代码
- import java.util.ArrayList;
- import java.util.List;
- /**
- * StringBuilder和StringBuffer的并发测验
- *
- * @Author: lingyejun
- * @Date: 2019/8/17
- * @Describe:
- * @Modified By:
- */
- public class SecurityCompare {
- public void stringBuilderTest() {
- // 初始化StringBuilder
- StringBuilder stringBuilder = new StringBuilder();
- // joinList
- List<StringBuilderThread> joinList = new ArrayList<>();
- // 模拟并发场景
- for (int i = 0; i < 1000; i++) {
- StringBuilderThread sbt = new StringBuilderThread(stringBuilder);
- sbt.start();
- joinList.add(sbt);
- }
- // 等待append线程执行完毕后再执行主线程
- for (StringBuilderThread thread : joinList) {
- try {
- thread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- // 打印最终的结果
- System.out.println("StringBuilder 并发append的结果: " + stringBuilder.length());
- }
- public void stringBufferTest() {
- // 初始化StringBuffer
- StringBuffer stringBuffer = new StringBuffer();
- // joinList
- List<StringBufferThread> joinList = new ArrayList<>();
- // 模拟并发场景
- for (int i = 0; i < 1000; i++) {
- StringBufferThread sbf = new StringBufferThread(stringBuffer);
- sbf.start();
- joinList.add(sbf);
- }
- // 等待append线程执行完毕后再执行主线程
- for (StringBufferThread thread : joinList) {
- try {
- thread.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- // 打印最终的结果
- System.out.println("StringBuffer 并发append的结果: " + stringBuffer.length());
- }
- public static void main(String[] args) {
- SecurityCompare securityCompare = new SecurityCompare();
- securityCompare.stringBuilderTest();
- securityCompare.stringBufferTest();
- }
- public static class StringBuilderThread extends Thread {
- private StringBuilder stringBuilder;
- public StringBuilderThread(StringBuilder stringBuilder) {
- this.stringBuilder = stringBuilder;
- }
- @Override
- public void run() {
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- stringBuilder.append("a");
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- private static class StringBufferThread extends Thread {
- private StringBuffer stringBuffer;
- public StringBufferThread(StringBuffer stringBuffer) {
- this.stringBuffer = stringBuffer;
- }
- @Override
- public void run() {
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- stringBuffer.append("a");
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
三、结论
- String为固定长度的字符串,StringBuilder和StringBuffer为变长字符串。
- StringBuffer是线程安全的,StringBuilder是非线程安全的。
- StringBuilder和StringBuffer的默认初始容量是16,可以提前预估好字符串的长度,进一步减少扩容带来的额外开销。
手把手实例对比String、StringBuilder字符串的连接效率及StringBuilder和StringBuffer线程安全的比较的更多相关文章
- 6.2 C++ string类型字符串的连接
参考:http://www.weixueyuan.net/view/6391.html 总结: 对于string类型变量,我们可以直接用“+”或者“+=”进行字符串的连接,操作符非常方便. 用“+”风 ...
- 从String类型字符串的比较到StringBuffer和StringBuilder
1. String类型 String类源码 为了从本质上理解String类型的特性所在,我们从String类型的源码看起,在源码中String类的注释中存在以下: /**Strings are con ...
- Java基础 -- 连接字符串时,使用+还是StringBuilder
结论 1-源代码中使用的+连接,实际上都使用的是StringBuilder. 2-用jad工具反编译,好处之一就是可以同时生成字节码和源代码.这样可以进行对照研究. ----------------- ...
- StringBuilder String string.Concat 字符串拼接速度再议
首先看测试代码: public class StringSpeedTest { "; public string StringAdd(int count) { string str = st ...
- StringBuilder String string.Concat 字符串拼接速度
首先看测试代码: public class StringSpeedTest { "; public string StringAdd(int count) { string str = st ...
- .NET面试题解析(03)-string与字符串操作
系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 字符串可以说是C#开发中最常用的类型了,也是对系统性能影响很关键的类型,熟练掌握字符串的操作非常重要. 常 ...
- String.format字符串拼接
一.String.Format1.简介 String类的format()方法用于创建格式化的字符串以及连接多个字符串对象. 2.参数 format()方法有两种重载形式. form ...
- C# 数据类型之 String(字符串)
Ø 简介 在开发中最常见的数据类型就是 String 类型,即字符串类型.为什么要单独讨论下这个类型,是因为:它是系统内置的基础数据类型:它的使用频率非常高:它是一个特殊的引用类型.其实大家都会使用 ...
- String拼接字符串效率低,你知道原因吗?
面试官Q1:请问为什么String用"+"拼接字符串效率低下,最好能从JVM角度谈谈吗? 对于这个问题,我们先来看看如下代码: public class StringTest { ...
随机推荐
- Java visualvm
简介 VisualVM是一个集成多个JDK命令行工具的可视化工具.可以作为Java应用程序性能分析和运行监控的工具.开发人员可以利用它来监控.分 析线程信息,浏览内存堆数据.系统管理员可以利用它来监测 ...
- iOS之集成GoogleMap定位、搜索注意事项
简介: 最近花了些时间看了GoogleMap官方文件并集成到国际版app中,网上关于GoogleMap for iOS的讲解相对Android来说少一点,比较有帮助的几乎全是英文文档.下面是我开发过程 ...
- 【开发工具】- Myeclipse10.7破解方法
1.下载myeclipse 10,如果没有,可以使用链接:https://pan.baidu.com/s/1l9juqD4ALMuepVL6e5kgjA 密码:kpx6:当然时间久了可能链接失效,如有 ...
- 对于flex布局的使用心得
弹性盒子flex: 对于客户端的布局非常有用,不管是平均分配space-around这个属性还是两端对齐space-betwee在页面布局的时候都会有很好的表现. 对于部分内容区域中,具有很多大致内容 ...
- Html-元素类型笔记
注意点: 元素类型分为 块级元素 和 行内元素 块级元素: 在网页中以块的形式显示,默认情况都会占据一行,两个相邻的块级元素不会出现并列显示的元素,按照顺序自上而下排列. 块级元素可以定义自己的宽度和 ...
- pychrom注册码
http://angame.top/wx/web/zhucema/ YZVR7WDLV8-eyJsaWNlbnNlSWQiOiJZWlZSN1dETFY4IiwibGljZW5zZWVOYW1lIjo ...
- 如何传递大文件(GB级别)
一.拆分:压缩工具,压缩并拆分为多个小文件. 二.QQ离线传输 QQ离线文件有限制条件: 1.离线传送的文件,为用户保存7天,逾期接收方不接收文件,系统将自动删除该文件: 2. 离线传送的文件,单个文 ...
- Linux操作系统的进程管理
Linux操作系统的进程管理 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.进程相关概念 1>.进程概述 内核的功用: 进程管理.文件系统.网络功能.内存管理.驱动程序. ...
- 完美快速解决百度分享不支持HTTPS的问题
百度分享不支持HTTPS这件事由来已久,我之前向百度分享提交过这个问题,无果!但近期themebetter主题用户咨询的比较多,我们就总结了解决方案. 第一步:下载百度分享必备文件 点此下载stati ...
- [AI] 切换cuda版本的万金油
1. 环境 ubuntu16.04 GTX1080Ti x 4 nvidia-418 cuda-10.1 pytorch1.0.0 目标:在最新的显卡驱动下,使用不同版本的cuda和深度学习框架来执行 ...