zoukankan      html  css  js  c++  java
  • String、StringBuffer和StringBuilder

    一、StringStringBuffer

            String类型和StringBuffer类型的主要性能差别事实上在于String是不可变的对象,因此在每次对String类型进行改变的时候事实上都等同于生成了一个新的String对象,然后将指针指向新的String对象,所以常常改变内容的字符串最好不要用String,由于每次生成对象都会对系统性能产生影响,特别当内存中无引用对象对了以后,JVMGC就会開始工作。那速度是一定会相当慢的。

    详细说原理的话。两个字符串相加,相当于运行了例如以下操作:

    Str1+str2运行了以下的过程:

    StringBuffer sb1 = new StringBufferstr1);

    sb1.append(str2);

    String result1 = sb1.toString();

    运行到最后,我们所须要的内容仅仅有result1这一个对象,中间出现的sb1就成为了垃圾回收的目标。

    此时,假设我们再加一个字符串的话……

    str1+str2+str3相当于在上面的基础上又运行了

    StringBuffersb2 = new StringBuffer(result1);

    sb2.append(str3);

    String result2 = sb2.toString();

    这时。对我们实用的对象仅仅有result2一个中间生成的对象都成为了垃圾回收的目标。假设继续追加下去。又会产生若干个StringBuffer的垃圾对象和String的垃圾对象。

    而假设是使用Stringbuffer类则结果就不一样了,每次结果都会对StringBuffer对象本身进行操作。而不是生成新的对象,再改变对象引用。

    StringBuffersb = new StringBuffer();

    sb.append(str1);

    sb.append(str2);

    ……

    sb.append(strN);

    String result = sb.toString();

    除了中间的一个StringBuffer对象最后会被弃掉,其它的都是有效对象,效率自然会高。

    package SE.AboutString;
    
    import java.util.Calendar;
    
    /**
     * 
     * <p>
     * Description: 測试String和StringBuffer的效率
     * </p>
     * @author zhangjunshuai
     * @version 1.0
     * Create Date: 2014-7-7 下午5:25:57
     * Project Name: UseTest
     *
     * <pre>
     * Modification History: 
      *             Date                                Author                   Version          Description 
     * -----------------------------------------------------------------------------------------------------------  
     * LastChange: $Date::             $      $Author: $          $Rev: $         
     * </pre>
     *
     */
    public class StringMerger {
    	
    	public static String merge(String[] strings, String separator, boolean isSepAtTail) {
    		if (strings == null) {
    			return null; 
    		}
    		if (separator == null) {
    			separator = ""; 
    		}
    		
    		String mergedString = new String(); 
    		for (int i = 0; i < strings.length - 1; i++) {
    			mergedString += (strings[i] + separator); 
    		}
    		
    		mergedString += strings[strings.length - 1]; 
    		if (isSepAtTail) {
    			mergedString += separator; 
    		}
    		
    		return mergedString; 
    	}
    	
    	
    	public static String bufferMerge(String[] strings, String separator, boolean isSepAtTail) {
    		if (strings == null) {
    			return null; 
    		}
    		if (separator == null) {
    			separator = ""; 
    		}
    		
    		StringBuffer mergeSb = new StringBuffer(); 
    		for (int i = 0; i < strings.length - 1; i++) {
    			mergeSb.append(strings[i]).append(separator); 
    		}
    		
    		mergeSb.append(strings[strings.length - 1]); 
    		if (isSepAtTail) {
    			mergeSb.append(separator); 
    		}
    		
    		return mergeSb.toString(); 
    	}
    	
    	public static void main(String[] args) {
    		// call the two methods to initialize the class and methods. 
    		System.out.println(StringMerger.merge(new String[] {"a", "kk", "ef"}, " ^_^ ", false)); 
    		System.out.println(StringMerger.bufferMerge(new String[] {"a", "kk", "ef"}, " ^_^ ", false));
    		
    		final int n = 10000;
    		final String sep = System.getProperty("line.separator");
    		
    		// create an array of String for merging.  
    		String[] forMerge = new String[n]; 
    		for (int i = 0; i < n; i++) {
    			forMerge[i] = Integer.toBinaryString(i); 
    		}
    		
    		// declare two variables to store start time and end time. 
    		long startTime = 0; 
    		long endTime = 0; 
    		
    		// run the test code 5 times. 
    		for (int i = 0; i < 5; i++) {
    			System.out.println("==="); 
    			
    			// get current time as start time. 
    			startTime = Calendar.getInstance().getTimeInMillis();
    			// merge string by using String. 
    			StringMerger.bufferMerge(forMerge, sep, false);
    			// get current time as end time. 
    			endTime = Calendar.getInstance().getTimeInMillis();
    			// print out the result. 
    			System.out.println("merge by StringBuffer start: " + startTime);
    			System.out.println("merge time: " + (endTime - startTime)); 
    			System.out.println("merge by StringBuffer end: " + endTime);
    			
    			// get current time as start time. 
    			startTime = Calendar.getInstance().getTimeInMillis();
    			// merge string by using StringBuffer. 
    			StringMerger.merge(forMerge, sep, false);
    			// get current time as end time. 
    			endTime = Calendar.getInstance().getTimeInMillis();
    			// print out the result. 
    			System.out.println("merge by String start: " + startTime);
    			System.out.println("merge time: " + (endTime - startTime)); 
    			System.out.println("merge by String end: " + endTime);
    		}
    	}
    }


    二、关于线程安全

    线程安全就是多个线程改动同一个对象时可能产生的冲突问题。比方有一个StringBuilder对象,变量名为stringBuffer,在一个线程里运行stringBuffer.append("0")的同一时候。另外一个线程也运行相同的代码,就有可能出现无法预料的问题。

    出现故障的原因是在StringBuilderappend方法中。不是仅仅有一条语句。而是由若干语句。

    当进程A进入append()函数时,还有一个线程B可能在当中随意一条语句之后就进入这个函数。从而再次运行函数中第一条语句,而接下来运行线程A中即将继续运行的语句还是运行线程B中即将运行的第二句谁也说不清。

            实际剖析一下,能够看到StringBuilder里面实际运行的语句例如以下(实际是运行父类的内容)

     

    if(str == null) str = "null";

    intlen = str.length();

    ensureCapacityInternal(count+ len);

    str.getChars(0,len, value, count);

    count+= len;

    returnthis;

    如果StringBuilder对象中的字符串长度为10。也就是count10。我们追加的字符串为“0”,也就是长度为1.如果当线程A运行到count+=len时候,恰好线程B的代码进入函数,而且取得运行权一直到结束,此时在线程B中的count由于加上了“0”的长度,为11.如今线程A再次開始运行,由于count的定义没有volatilekeyword,所以非常有可能线程A中的count还是之前的10,所以再次运行语句时候,让count变成11.结果命名之行了两次append()函数,count却仅仅添加了1.显然与期望逻辑不符。

    由此可知StringBuffer是线程安全的可变字符序列。可将字符串缓冲区安全的用于多个线程。能够在必要时对这些方法进行同步,因此随意特定实例上的全部操作就好像是以串行顺序发生的。该顺序与所涉及的每一个线程进行的方法调用顺序一致。StringBuffer上的主要操作时appendinsert方法,可重载这些方法。以接受随意的类型的数据。每一个方法都能效地将给定的数据转换成字符串。然后将该字符串的字符追加或插入到字符串缓冲区中。append方法始终将这些字符加入到缓冲区的末端。而inert方法则在指定的点加入字符。

    StringBuffer是一个可变的字符序列是5.0新增的,此类提供一个与StringBuffer兼容的API。但不保证同步。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。假设可能。简易优先採用该类,由于在大多数实现中。它比StringBuffer要快。

    两者的方法基本同样。

    package SE.AboutString;
    
    import java.util.Random;
    
    public class StringBufferVsStringBuilder {
    	public static int demo(final Object stringJoiner, final int testCount) throws InterruptedException {
    		ThreadGroup group = new ThreadGroup(stringJoiner.getClass().getName() + "@" + stringJoiner.hashCode()); 
    		final Random rand = new Random(); 
    		
    		Runnable listAppender = new Runnable() {
    			public void run() {
    				try {
    					Thread.sleep(rand.nextInt(2));
    				} catch (InterruptedException e) {
    					return; 
    				} 
    				if (stringJoiner instanceof StringBuffer) {
    					((StringBuffer)stringJoiner).append("0");
    				} else if (stringJoiner instanceof StringBuilder) {
    					((StringBuilder)stringJoiner).append("0"); 
    				}
    			}
    		}; 
    		
    		for (int i = 0; i < testCount; i++) {
    			new Thread(group, listAppender, "InsertList-" + i).start(); 
    		}
    		
    		while (group.activeCount() > 0) {
    			Thread.sleep(10); 
    		}
    		
    		return stringJoiner.toString().length(); 
    	}
    	
    	public static void main(String[] args) throws InterruptedException {
    		StringBuilder stringBuilder = new StringBuilder();
    		StringBuffer stringBuffer = new StringBuffer();
    		final int N = 10000; 
    		for (int i = 0; i < 10; i++) {
    			stringBuilder.delete(0, stringBuilder.length()); 
    			stringBuffer.delete(0, stringBuffer.length()); 
    			int builderLength = demo(stringBuilder, N); 
    			int bufferLength = demo(stringBuffer, N); 
    			System.out.println("StringBuilder/StringBuffer: " + builderLength + "/" + bufferLength); 
    		}
    	}
    }
    
    
    // Output will be something like this:
    // StringBuilder/StringBuffer: 9995/10000
    // StringBuilder/StringBuffer: 9996/10000
    // StringBuilder/StringBuffer: 9998/10000
    // StringBuilder/StringBuffer: 9997/10000
    // StringBuilder/StringBuffer: 9995/10000
    // StringBuilder/StringBuffer: 9996/10000
    // StringBuilder/StringBuffer: 9998/10000
    // StringBuilder/StringBuffer: 9998/10000
    // StringBuilder/StringBuffer: 9999/10000
    // StringBuilder/StringBuffer: 9999/10000



    package SE.AboutString;
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.Random;
    
    public class ThreadSafeDemo {
    	public static int demo(final List list, final int testCount) throws InterruptedException {
    		ThreadGroup group = new ThreadGroup(list.getClass().getName() + "@" + list.hashCode()); 
    		final Random rand = new Random(); 
    		
    		Runnable listAppender = new Runnable() {
    			public void run() {
    				try {
    					Thread.sleep(rand.nextInt(2));
    				} catch (InterruptedException e) {
    					return; 
    				} 
    				list.add("0"); 
    			}
    		}; 
    		
    		for (int i = 0; i < testCount; i++) {
    			new Thread(group, listAppender, "InsertList-" + i).start(); 
    		}
    		
    		while (group.activeCount() > 0) {
    			Thread.sleep(10); 
    		}
    		
    		return list.size(); 
    	}
    	public static void main(String[] args) throws InterruptedException {
    		List unsafeList = new ArrayList(); 
    		List safeList = Collections.synchronizedList(new ArrayList()); 
    		final int N = 10000; 
    		for (int i = 0; i < 10; i++) {
    			unsafeList.clear(); 
    			safeList.clear(); 
    			int unsafeSize = demo(unsafeList, N); 
    			int safeSize = demo(safeList, N); 
    			System.out.println("unsafe/safe: " + unsafeSize + "/" + safeSize); 
    		}
    	}
    }
    
    /*unsafe/safe: 9896/10000
    unsafe/safe: 9931/10000
    unsafe/safe: 9940/10000
    unsafe/safe: 9912/10000
    unsafe/safe: 9960/10000
    unsafe/safe: 9954/10000
    unsafe/safe: 9960/10000
    unsafe/safe: 9944/10000
    unsafe/safe: 9960/10000
    unsafe/safe: 9957/10000*/

    參考:

    http://www.zhihu.com/question/20101840

    http://blog.csdn.net/rmn190/article/details/1492013

     

     

     

     

     

     

    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    决策树之C4.5算法
    决策树之ID3算法
    AndroidStudio 3.4.2配置 Opencv 3.7
    Android 实现在ImageView上绘图
    Opencv 对比度增强 C++
    Opencv对比度增强 python API
    hive中与hbase外部表join时内存溢出(hive处理mapjoin的优化器机制)
    hive的数据导入与数据导出:(本地,云hdfs,hbase),列分隔符的设置,以及hdfs上传给pig如何处理
    hive的map类型处理
    pig的udf编写
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4635980.html
Copyright © 2011-2022 走看看