实例的对比StringStringBuilder字符串的连接效率及StringBuilder和StringBuffer线程安全的比较
Posted lingyejun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实例的对比StringStringBuilder字符串的连接效率及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.确认是否需要进行扩容操作
2.1 最小所需容量minimumCapacity是否比原数组长度要长,即当原数组长度不能满足所需最小容量时进行扩容操作。
2.2 计算扩容之后的容量newCapacity,newCapacity = (value.length * 2) + 2。
2.3 扩容后是否还小于所需的最小容量,如果小于则直接设置新容量为最小所需容量minimumCapacity。
2.4 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();
三、结论
1.String为固定长度的字符串,StringBuilder和StringBuffer为变长字符串。
2.StringBuffer是线程安全的,StringBuilder是非线程安全的。
3.StringBuilder和StringBuffer的默认初始容量是16,可以提前预估好字符串的长度,进一步减少扩容带来的额外开销。
以上是关于实例的对比StringStringBuilder字符串的连接效率及StringBuilder和StringBuffer线程安全的比较的主要内容,如果未能解决你的问题,请参考以下文章
StringStringBuilder和StringBuffer
StringStringBuilder和StringBuffer
[转帖]StringStringBuilder与StringBuffer