StringBuider 在什么条件下、如何使用效率更高_相关技巧

来源:脚本之家  责任编辑:小易  

这个东西的存在看似很没有道理但是其实是这样的.举个例子:你拼字符串一般是怎么拼呢?String city="上海;String a="您好,欢迎来到"+city;是吧?但是这样做的意思是什么?电脑会在内存中令开辟一块内存空间,实例化一个新的String类来保存它.如果有大量字符串操作,这么做肯定是很消耗内存的.并且效率低下所以,StringBuilder出现了.他不能+,但是它有Append和Remove等函数来实现拼接和删除字符串.在操作字符串的时候,他不会再重新实例化一个类这样可以节省内存,提高效率。当然,他还有N多函数.可以对字符串作各种操作.可以说是一个字符串工具箱!你还是查一下MSDN吧(JAVA里也有这个.类似的还有StringBuffer参见JAVA API)www.zgxue.com防采集请勿采集本网。

引言

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

using system.text;反正就这样。你自己加上这个命名空间就行了

测试用例

stringBuilder很不错的,完全可以替代string,最主要的是它的效率高 必须先实例化一个 stringBuilder sBuilder=new stringBuilder();您可以拼接执行SQL语句,易于阅读 sBuilder.Append(\"select*

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

StringBuilder 字符串变量(非线程安全) 简要的说,String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象,因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的

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

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

第一组:

在每次 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 的效率是一样的!为什么呢?

使用 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 两者的效率一样。后者写起来还方便...

到此这篇关于StringBuider 在什么条件下、如何使用效率更高?的文章就介绍到这了,更多相关StringBuider如何使用效率更高内容请搜索真格学网以前的文章或继续浏览下面的相关文章希望大家以后多多支持真格学网!

简要的说,String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象(为什么?问问 Java 的设计者吧,为什么 String 不是原生类型呢?因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM 的 GC 就会开始工作,那速度是一定会相当慢的。这里尝试举个不是很恰当的例子:String S1=“abc”;For(int I=0;I;I+)/For 模拟程序的多次调用{S1+“def”;S1=“abc”;}如果是这样的话,到这个 for 循环完毕后,如果内存中的对象没有被 GC 清理掉的话,内存中一共有 2 万多个了,惊人的数目,而如果这是一个很多人使用的系统,这样的数目就不算很多了,所以大家使用的时候一定要小心。而如果是使用 StringBuffer 类则结果就不一样了,每次结果都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下我们推荐使用 StringBuffer,特别是字符串对象经常改变的情况下。而在某些特别情况下,String 对象的字符串拼接其实是被 JVM 解释成了 StringBuffer 对象的拼接,所以这些时候 String 对象的速度并不会比 StringBuffer 对象慢,而特别是以下的字符串对象生成中,String 效率是远要比 StringBuffer 快的:String S1=“This is only a”+“simple”+“test”;StringBuffer Sb=new StringBuilder(“This is only a”).append(“simple”).append(“test”);你会很惊讶的发现,生成 String S1 对象的速度简直太快了,而这个时候 StringBuffer 居然速度上根本一点都不占优势。其实这是 JVM 的一个把戏,在 JVM 眼里,这个String S1=“This is only a”+“simple”+“test”;其实就是:String S1=“This is only a simple test”;所以当然不需要太多的时间了。但大家这里要注意的是,如果你的字符串是来自另外的 String 对象的话,速度就没那么快了,譬如:String S2=“This is only a”;String S3=“simple”;String S4=“test”;String S1=S2+S3+S4;这时候 JVM 会规规矩矩的按照原来的方式去做,S1 对象的生成速度就不像刚才那么快了,一会儿我们可以来个测试作个验证。由此我们得到第一步结论:在大部分情况下 StringBuffer>String而 StringBuilder 跟他们比又怎么样呢?先简单介绍一下,StringBuilder 是 JDK5.0 中新增加的一个类,它跟 StringBuffer 的区别看下面的介绍(来源 JavaWorld):Java.lang.StringBuffer 线程安全的可变字符序列。类似于 String 的字符串缓冲区,但不能修改。可将字符串缓冲区安全地用于多个线程。可以在必要时对这些方法进行同步,因此任意特定实例上的所有操作就好像是以串行顺序发生的,该顺序与所涉及的每个线程进行的方法调用顺序一致。每个字符串缓冲区都有一定的容量。只要字符串缓冲区所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区数组。如果内部缓冲区溢出,则此容量自动增大。从 JDK 5.0 开始,为该类增添了一个单个线程使用的等价类,即 StringBuilder。与该类相比,通常应该优先使用 StringBuilder 类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。但是如果将 StringBuilder 的实例用于多个线程是不安全的。需要这样的同步,则建议使用 StringBuffer。这样说估计大家都能明白他们之间的区别了,那么下面我们再做一个一般性推导:在大部分情况下 StringBuilder>StringBuffer因此,根据这个不等式的传递定理:在大部分情况下 StringBuilder>StringBuffer>String既然有这样的推导结果了,我们做个测试验证一下:测试代码如下:public class testssb {Creates a new instance of testssb*/final static int ttime=10000;测试循环次数public testssb(){}public void test(String s){long begin=System.currentTimeMillis();for(int i=0;i;i+){s+"add;}long over=System.currentTimeMillis();System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");}public void test(StringBuffer s){long begin=System.currentTimeMillis();for(int i=0;i;i+){s.append("add");}long over=System.currentTimeMillis();System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");}public void test(StringBuilder s){long begin=System.currentTimeMillis();for(int i=0;i;i+){s.append("add");}long over=System.currentTimeMillis();System.out.println("操作"+s.getClass().getName()+"类型使用的时间为:"+(over-begin)+"毫秒");}对 String 直接进行字符串拼接的测试public void test2(){String s2="abadf;long begin=System.currentTimeMillis();for(int i=0;i;i+){String s=s2+s2+s2;}long over=System.currentTimeMillis();System.out.println("操作字符串对象引用相加类型使用的时间为:"+(over-begin)+"毫秒");}public void test3(){long begin=System.currentTimeMillis();for(int i=0;i;i+){String s="abadf"+"abadf"+"abadf;}long over=System.currentTimeMillis();System.out.println("操作字符串相加使用的时间为:"+(over-begin)+"毫秒");}public static void main(String[]args){String s1="abc;StringBuffer sb1=new StringBuffer("abc");StringBuilder sb2=new StringBuilder("abc");testssb t=new testssb();t.test(s1);t.test(sb1);t.test(sb2);t.test2();t.test3();}}以上代码在 NetBeans 5.0 IDE/JDK1.6 上编译通过,循环次数 ttime 为 10000 次的测试结果如下:操作 java.lang.String 类型使用的时间为:4392 毫秒操作 java.lang.StringBuffer 类型使用的时间为:0 毫秒操作 java.lang.StringBuilder 类型使用的时间为:0 毫秒操作字符串对象引用相加类型使用的时间为:15 毫秒操作字符串相加使用的时间为:0 毫秒好像还看不出 StringBuffer 和 StringBuilder 的区别,把 ttime 加到 30000 次看看:操作 java.lang.String 类型使用的时间为:53444 毫秒操作 java.lang.StringBuffer 类型使用的时间为:15 毫秒操作 java.lang.StringBuilder 类型使用的时间为:15 毫秒操作字符串对象引用相加类型使用的时间为:31 毫秒操作字符串相加使用的时间为:0 毫秒StringBuffer 和 StringBuilder 的性能上还是没有太大的差异,再加大到 100000 看看,这里就不加入对 String 类型的测试了,因为对 String 类型这么大数据量的测试会很慢滴…操作 java.lang.StringBuffer 类型使用的时间为:31 毫秒操作 java.lang.StringBuilder 类型使用的时间为:16 毫秒能看出差别了,但其中有多次的测试结果居然是 StringBuffer 比 StringBuilder 快,再加大一些到 1000000 看看(应该不会当机吧?操作 java.lang.StringBuffer 类型使用的时间为:265 毫秒操作 java.lang.StringBuilder 类型使用的时间为:219 毫秒有些少区别了,而且结果很稳定,再大点看看,ttime=5000000:Exception in thread"main"java.lang.OutOfMemoryError:Java heap space·呵呵,算了,不去测试了,基本来说都是在性能上都是 StringBuilder>StringBuffer>String 的了内容来自www.zgxue.com请勿采集。


  • 本文相关:
  • 深入解析java编程中的stringbuffer与stringbuider
  • 奇怪的回车换行问题
  • eclipse 格式化代码时不换行与自动换行的实现方法
  • https的七个误解
  • 漂流瓶推送需求的逻辑实现代码
  • git里多种撤销操作的最佳方法
  • json转string与string转json及判断对象类型示例代码
  • zend stdio8.0 快捷键汇总集合
  • 网站登录持久化cookie方案
  • web 开发中遇到的utf-8编码的问题总结
  • 30个提高web程序执行效率的好经验分享
  • StringBuilder是什么类型啊???
  • StringBuilder是什么意思
  • string和stringbuilder的区别
  • StringBuilder是什么意思
  • c# stringbuilder 在哪个空间 怎么引用
  • stringBuilder怎么用?
  • java中的StringBuilder有什么用?
  • C#string和StringBuilder的区别
  • 网站首页网页制作脚本下载服务器操作系统网站运营平面设计媒体动画电脑基础硬件教程网络安全javascriptasp.netphp编程ajax相关正则表达式asp编程jsp编程编程10000问css/htmlflex脚本加解密web2.0xml/rss网页编辑器相关技巧安全相关网页播放器其它综合dart首页深入解析java编程中的stringbuffer与stringbuider奇怪的回车换行问题eclipse 格式化代码时不换行与自动换行的实现方法漂流瓶推送需求的逻辑实现代码git里多种撤销操作的最佳方法json转string与string转json及判断对象类型示例代码zend stdio8.0 快捷键汇总集合网站登录持久化cookie方案web 开发中遇到的utf-8编码的问题总结30个提高web程序执行效率的好经验分享2019最新的pycharm激活码(推荐)进制转换算法原理(二进制 八进制eclipse 格式化代码时不换行与自微信小程序设置http请求的步骤详几款开源的中文分词系统十进制负数转换为二进制、八进制从console.log说起(console.log详url 长度有限制吗?git 撤销操作、删除文件和恢复文gvim, gvim easy, gvim read-only 的简单详解https 加密完整过程git 教程之工作流程详解git提交空目录的方法算法系列15天速成 第七天 线性表【上】在命令行用 sort 进行排序的方法git ssh 配置及使用方法使用swipe方法模拟屏幕滑动与手势密码绘制网站程序员如何应对web标准对web开发人员有用的8个网站小结
    免责声明 - 关于我们 - 联系我们 - 广告联系 - 友情链接 - 帮助中心 - 频道导航
    Copyright © 2017 www.zgxue.com All Rights Reserved