String高效编程优化(Java)

1, substring截取超大字符串可能形成的“内存泄漏”html

2,+ 操做符的优化和局限算法

3,StringBuilder和StringBuffer编程

4,split和StringTokenizer作简单字符分割效率的比较数组

 

 

1, substring截取超大字符串可能形成的“内存泄漏”

咱们知道,String对象内保存着一个char数组。可是char数组未必和String所表明的字符集等长,而多是一个“超集”。String有一个私有的构造函数:安全

// Package private constructor which shares value array for speed.
    String(int offset, int count, char value[]) {
        this.value = value;
        this.offset = offset;
        this.count = count;
    }

 

这个构造函数容许你只使用value[]的一部分做为String的字符集,它并不会截取value[]的一部分来建立一个新的char数组,而是把它整个保存起来了。多线程

接着来看substring函数的实现:app

     public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > count) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        if (beginIndex > endIndex) {
            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
        }
        return ((beginIndex == 0) && (endIndex == count)) ? this :
            
new String(offset + beginIndex, endIndex -
 beginIndex, value);
    }

 

substring正是用咱们上面提到的构造函数来构造返回的String的,Java这么作有利有弊:框架

1)若是咱们要从一个大字符串中截取许多小字符串,那么这些小字符串共享一个大的char[]。那么,这么作是很是高效的,避免了从新分配内存的时间空间开销。异步

2)可是,若是咱们只从中截取一个或少数几个很小的字符串,原String将丢弃,而这些小字符串却被长期保存,这样咱们就形成了某种意义上的内存泄漏 -- 咱们觉得原String的内存被GC释放了,然而并无,它的主要部分 — 巨大的char数组仍被他的子String引用着,虽然只有其中很小的一部分被它们使用了。ide

对于这种泄漏,解决办法很简单,使用如下语法

str2 = new String(str1.substring(5,100));

 

构造函数String(String)会为新的String建立一个新的char[]。可是前提是,咱们意识到了substring可能致使的问题。

 

(后记:

谢谢@╰︶赖床专业户こ 的提醒,JDK7早期的版本仍是按照我说的方式实现的,可是后期的版本已经修复了。我没有注意我参考的版本。
应该说新的实现有利有弊,如今咱们须要担忧的不是substring的泄露问题,而是效率问题了。
很是赞扬你的研究精神。

 

 

2,+ 操做符的优化和局限

咱们知道,对于如下语法:

str1 += "abc";
str1 = str1 + "abc";

 

Java将建立一个新的String对象和字符串数组,把原字符串和”abc”拷贝拼接到新的字符串数组中。若是反复进行这样字符串的累加操做,天然是很是低效的,这种状况按照最佳实践,应该使用StringBuilder。

但事实上,Java已经对+操做进行了优化。看下面的代码:

String temp = "ABC" + 200 + 'D';

 

编译器已经把该代码优化编译成了:

String temp = new StringBuilder().append( "ABC" ).append( 200 ).append('D').toString();

(注:

另外,若是代码简单的多个字符串相加:

String temp = "Hello" + “ ” + “World”;

编译器直接优化为

String temp = "Hello World”;

 

因此,连续累加效率并不比使用StringBuilder效率差,由于它原本就是用一个StringBuilder对象连续的append来实现的。

可是,若是是:

for(int i=0; i<100; i++)
{
    temp+="abc";
}

 

编译器并无办法把以上for循环里面屡次迭代的‘+’操做优化为只使用一个StringBuilder对象的连续append操做。所以,仍是很是低效的。

 

简而言之,若是全部的字符串拼接能够在一行里面用‘+’完成,那么是没有效率问题的;不然,最好使用StringBuilder。

 

 

 

3,StringBuilder和StringBuffer

StringBuilder和StringBuffer用法基本没什么区别,可是StringBuilder不是线程安全的,StringBuffer是线程安全的。StringBuffer在全部用于字符操做的public方法都加了锁--使用了synchronized关键字。

咱们来测试一下单线程下StringBuilder和StringBuffer的效率,如下代码:

public static void main(String[] args){
        long t1 = System.nanoTime();
        StringBuffer stringBuffer = new StringBuffer();
        
        for(int i=0; i<1000000; i++)
        {
            stringBuffer.append("a");
        }
        stringBuffer.toString();
        long t2 = System.nanoTime();
        System.out.println("StringBuffer :"+ (t2-t1));
        
         t1 = System.nanoTime();
        StringBuilder stringBuilder = new StringBuilder();
        
        for(int i=0; i<1000000; i++)
        {
            stringBuilder.append("a");
        }
        stringBuilder.toString();
         t2 = System.nanoTime();
        System.out.println("StringBuilder:"+ (t2-t1));
    }

结果:

StringBuffer :33979818
StringBuilder:14061978

单线程状况下,StringBuilder要快一倍多。

 

那多线程状况StringBuffer效率如何呢?下面代码测试:

long t1 = System.nanoTime();
        final StringBuffer stringBuffer = new StringBuffer();
        
        ExecutorService executor = Executors.newFixedThreadPool(3);
        CountDownLatch countDownLatch = new CountDownLatch(3);
        
        for (int i = 0; i < 3; i++) {
            executor.execute(new Runnable() {

                @Override
                public void run() {
                    for (int i = 0; i < 333333; i++) {
                        stringBuffer.append("a");
                    }
                }

            });
            countDownLatch.countDown();
        }
        stringBuffer.toString();
        countDownLatch.await();
        
        long t2 = System.nanoTime();
        System.out.println("StringBuffer :"+ (t2-t1));

 

结果:

StringBuffer :2603076

 

虽然咱们使用了3个工做线程,可是效率几乎比单线程没有什么提高,这就是使用锁在多线程的结果--锁在多线程中的协调,致使线程的频繁切换,大大下降效率。

虽然我实在不知道有什么场景须要用到多线程的字符串拼装。假设有,而且对性能有很严格的要求,我以为能够考虑使用一些无锁的多线程编程框架,例如Disruptor--一个无锁的RingBuffer框架,使用多个生产者线程往Ring buffer中投递String对象,在消费者中用StringBuilder进行组装。(相似log4j 2的异步日志处理)

 

 

 

 

4,split和StringTokenizer作简单字符分割效率的比较。

不少文章都说split比StringTokenizer效率高不少,开始也深觉得然,可是却发现它们的测试代码都存在很严重的问题。本身作了一下测试

        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 1000000; i++) {
            stringBuilder.append(i);
            stringBuilder.append(",");
        }
        
        
        String str = stringBuilder.toString();
        long t1 = System.nanoTime();
        String[] strArray = str.split(",");
        long t2 = System.nanoTime();
        System.out.println("split :" + (t2 - t1));

        String str1 = stringBuilder.toString();
        t1 = System.nanoTime();
        StringTokenizer stringTokenizer = new StringTokenizer(str1, ",");
        //List<String> strList = new ArrayList<String>(1000000); //或者 String[] strArray1 = new String[stringTokenizer.countTokens()];
        for (int i = 0; i < 1000000; i++) {
            String subStr = stringTokenizer.nextToken();
            //strList.add(subStr); //或者strArray1[i] =subStr;
        }
        t2 = System.nanoTime();
        System.out.println("token :" + (t2 - t1));

 

结果:

split :248539389
token :53191452

 

StringTokenizer 比split快4倍。

可是上面的比较在某些状况下并不公平,split会返回一个数组,而StringTokenizer 的next方法只能逐个浏览token。若是要求StringTokenizer 也把返回的子字符串保存在List中,那么结果如何呢?把上面代码段中的注释掉的代码打开,使StringTokenizer 也要把tokens保存在List或Array中,再进行测试。

结果:

split :254496592
token :303926083

 

这种状况下StringTokenizer 的效率还差一些。所以,不能一律而论split或StringTokenizer 谁的效率高,还要看若是使用。若是须要把结果放在Array或List当中,split更简单还有效率。(可见2种算法效率并无本质差异,差就差在Array或List的使用上,具体还要从JDK的源代码去分析)

 

Binhua Liu原创文章,转载请注明原地址http://www.cnblogs.com/Binhua-Liu/p/5572350.html

相关文章
相关标签/搜索