首页 » PHP教程 » phpstringbuilder技巧_老轨范员浅谈StringBuilder

phpstringbuilder技巧_老轨范员浅谈StringBuilder

访客 2024-11-18 0

扫一扫用手机浏览

文章目录 [+]

public class StringTest { public static void main(String[] args) { String str1 = \"大众hello \公众; String str2 = \"大众java\"大众; String str3 = str1 + str2 + \"大众!\公众; String str4 = new StringBuilder().append(str1).append(str2).append(\"大众!\"大众).toString(); }}

上述 str3 和 str4 的实行效果实在是一样的,不过在for循环中,千万不要利用 \"大众+\"大众 进行字符串拼接。

public class test { public static void main(String[] args) { run1(); run2(); } public static void run1() { long start = System.currentTimeMillis(); String result = \公众\公众; for (int i = 0; i < 10000; i++) { result += i; } System.out.println(System.currentTimeMillis() - start); } public static void run2() { long start = System.currentTimeMillis(); StringBuilder builder = new StringBuilder(); for (int i = 0; i < 10000; i++) { builder.append(i); } System.out.println(System.currentTimeMillis() - start); }}

在for循环中利用 \"大众+\"大众 和StringBuilder进行1万次字符串拼接,耗时情形如下:

phpstringbuilder技巧_老轨范员浅谈StringBuilder

1、利用 \"大众+\"大众 拼接,均匀耗时 250ms;

phpstringbuilder技巧_老轨范员浅谈StringBuilder
(图片来自网络侵删)

2、利用StringBuilder拼接,均匀耗时 1ms;

for循环中利用 \公众+\"大众 拼接为什么这么慢?下面是run1方法的字节码指令

5 ~ 34 行对应for循环的代码,可以创造,每次循环都会重新初始化StringBuilder工具,导致性能问题的涌现。

性能问题

StringBuilder内部掩护了一个char[]类型的value,用来保存通过append方法添加的内容,通过 new StringBuilder() 初始化时,char[]的默认长度为16,如果append第17个字符,会发生什么?

void expandCapacity(int minimumCapacity) { int newCapacity = value.length 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity);}

如果value的剩余容量,无法添加全部内容,则通过expandCapacity(int minimumCapacity)方法对value进行扩容,个中minimumCapacity = 原value长度 + append添加的内容长度。

1、扩大容量为原来的两倍 + 2,为什么要 + 2,而不是刚好两倍?

2、如果扩容之后,还是无法添加全部内容,则将 minimumCapacity 作为终极的容量大小;

3、利用 System.arraycopy 方法对原value数据进行复制;

在利用StringBuilder时,如果给定一个得当的初始值,可以避免由于char[]数组多次复制而导致的性能问题。

不同初始容量的性能测试:

public class StringBuilderTest { public static void main(String[] args) { int sum = 0; final int capacity = 40000000; for (int i = 0; i < 100; i++) { sum += cost(capacity); } System.out.println(sum / 100); } public static long cost(int capacity) { long start = System.currentTimeMillis(); StringBuilder builder = new StringBuilder(capacity); for (int i = 0; i < 10000000; i++) { builder.append(\"大众java\"大众); } return System.currentTimeMillis() - start; }}

实行一千万次append操作,不同初始容量的耗时情形如下:

1、容量为默认16时,均匀耗时110ms;

2、容量为40000000时,不会发生复制操作,均匀耗时85ms;

通过以上数据可以创造,性能损耗不是很严重。

内请安题

1、StringBuilder内部进行扩容时,会新建一个大小为原来两倍+2的char数组,并复制原char数组到新数组,导致内存的花费,增加GC的压力。

2、StringBuilder的toString方法,也会造成char数组的摧残浪费蹂躏。

public String toString() { // Create a copy, don't share the array return new String(value, 0, count);}

String的布局方法中,会新建一个大小相等的char数组,并利用 System.arraycopy() 复制StringBuilder中char数组的数据,这样StringBuilder的char数组就白白摧残浪费蹂躏了。

重用StringBuilder

public class StringBuilderHolder { private final StringBuilder sb; public StringBuilderHolder(int capacity) { sb = new StringBuilder(capacity); } public StringBuilder resetAndGet() { sb.setLength(0); return sb; }}

通过 sb.setLength(0) 方法可以把char数组的内存区域设置为0,这样char数组重复利用,为了避免并发访问,可以在ThreadLocal中利用StringBuilderHolder,利用办法如下:

private static final ThreadLocal<StringBuilderHolder> stringBuilder= new ThreadLocal<StringBuilderHolder>() { @Override protected StringBuilderHolder initialValue() { return new StringBuilderHolder(256); }}; StringBuilder sb = stringBuilder.get().resetAndGet();

不过这种办法也存在一个问题,该StringBuilder实例的内存空间一贯不会被GC回收,如果char数组在某次操作中被扩容到一个很大的值,可能之后很长一段韶光都不会用到如此大的空间,就会造成内存的摧残浪费蹂躏。

总结

虽然利用默认的StringBuilder进行字符串拼接操作,性能花费不是很严重,但在高性能场景下,还是推举利用ThreadLocal下可重用的StringBuilder方案。

相关文章

介绍白点控制之路,从原理到方法

白点,作为生活中常见的现象,无处不在。对于如何控制白点,许多人却感到困惑。本文将从原理出发,探讨白点的控制方法,并结合实际案例,为...

PHP教程 2025-01-03 阅读1 评论0

介绍直播王者,如何开启你的电竞直播之旅

随着电竞产业的蓬勃发展,越来越多的年轻人投身于电竞直播行业。王者荣耀作为一款备受欢迎的MOBA手游,吸引了大量玩家和观众。如何开启...

PHP教程 2025-01-03 阅读1 评论0