java性能优化读书笔记之三《程序优化===字符串优化》

约定(基于JDK1.6)

1. String字符串优化

String对象是java重要的数据类型。它不属于java基础类型。通过查看jdk源码分析,可以得到String是由字符数组、偏移量、字符串长度组成。

String对象的特点

  1. 不变性
  2. 针对常量的优化
  3. 类的final定义

这里写图片描述

不变性
String对象实例化后,是不能被修改的。这有利于在多线程环境下,省略了同步的机制。大幅度的提高了性能

针对常量的优化
当两个字符串拥有相同的值,它们只是常量池的一个拷贝。

String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
System.out.println(str1==str2);//返回ture
System.out.println(str1==str3);//返回false
System.out.println(str1==str2.intern());//返回ture

内存分析
这里写图片描述

类的final定义
1. final类不能被继承,没有子类,final类中的方法默认是final的。
2. final方法不能被子类的方法覆盖,但可以被继承。
3. final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
4. final不能用于修饰构造方法。

2. subString方法内存泄露

substring(beginIndex, endIndex)方法是String对象用来截取子字符串。beginIndex参数表示开始,结束于endIndex-1。

代码清单1.1:

package org.com.program.string;

public class SubString02 {

    public static void main(String[] args) {
        String str ="abcdefg";
        System.out.println(str.substring(0, 3));
    }

内存模型分析
这里写图片描述

JDK1.6中存在严重的内存泄露
源码清单2.1:

  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);
    }
 // 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;
    }

从源码分析得出:原始的字符串的字符数组对象全部复制给子字符串的字符数组对象(this.value = value;),只是修改了偏移量以及字符数组的大小(通过偏移量和字符数组的大小能定位到字符串的内容)。这样做的坏处在于原始的字符串的字符数组过大,且截取该字符串的频率过多。会导致内存溢出的情况,以下模拟内存溢出以及解决方式

代码清单2.2

package org.com.program.string;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;



/**
 * String的subString方法内存溢出模拟
 * @author Administrator
 *
 */

public class SubString02 {

    private  String largeString = new String(new byte[10000000]); 

     String getString() { 
        return largeString.substring(0,2); 
    } 

    @Test
    public void  test01() { 
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 1000000; i++) { 
            SubString02 gc = new SubString02(); 
            // list.add(gc.getString()); 内存溢代码
            list.add(new String(gc.getString()));//解决内存溢出的代码
        } 
    } 
}

3. 字符串分割和查找和查找

String分割是根据分割标识分割成不同的子字符串。以下为StringTokenizer、split()、indexOf()三种分割方式性能比较:
代码清单3.1:

package org.com.program.string;

import java.util.StringTokenizer;
/**
 *
 * auth jian.liu
 *
 */
public class StringSplit03 {

    public static void main(String[] args) {
        String str = buildString(1000000); //1.7新特性, 1000000
        long start;
        long end;

        System.out.println("-----------StringTokenizer start-----------");      
        start = System.currentTimeMillis();
        StringTokenizer st = new StringTokenizer(str,";");
        StringBuilder sb = new StringBuilder();
        while(st.hasMoreTokens()){
            sb.append(st.nextToken());
        }
        end = System.currentTimeMillis();
        System.out.println("StringTokenizer time use:" + (end-start));

        System.out.println("-----------StringSpilt start-----------");      
        start = System.currentTimeMillis();
        StringBuilder sb2 = new StringBuilder();
        String[] strs = str.split(";");
        for(String s: strs){
            sb2.append(s);
        }
        end = System.currentTimeMillis();
        System.out.println("StringSpilt time use:" + (end-start));  

        start = System.currentTimeMillis();

        System.out.println("-----------indexof start-----------");   
        String temp = str;
        while(true) {
            int j = temp.indexOf(';');
            if(j < 0) break;
            String subStr = temp.substring(0,j);
            temp = temp.substring(j+1,temp.length());
        }

        end = System.currentTimeMillis();
        System.out.println("indexOf time use:" + (end-start));  
        System.out.println("-----------indexof end-----------");   
    }

    //建立一个长字符串,
    private static String buildString(int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i <length;i++ ){
            sb.append(i);
            sb.append(";");
        }       
        return sb.toString();
    }
}

性能参数参考:
这里写图片描述

4. 字符串查找

在编写代码时,经常判断字符串是否已“xxx”子字符串开头的逻辑。一般我们用String对象的startWith()方法,但是此方法相比charAt()方法效率低。以下为两者之间性能比较:
代码清单4.1:

package org.com.program.string;

/**
 * 字符串查找性能对比
 * @author jian.liu
 *
 */
public class StringFind04 {

    public static void main(String[] args) {
        String str = buildString(10000);
        long start;
        long end;

        start = System.currentTimeMillis();
        for(int i=0; i<1000000000; i++) {
            if(str.charAt(0) == 'a') {
                System.out.println(str.charAt(0));
            }
        }

        end = System.currentTimeMillis();
        System.out.println("charAt of time{}:"+(end-start));

        start = System.currentTimeMillis();
        for(int i=0; i<1000000000; i++) {
            str.startsWith("a");
        }

        end = System.currentTimeMillis();
        System.out.println("startWith of time{}:"+(end-start));

    }

    //建立一个长字符串,
    private static String buildString(int length) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i <length;i++ ){
            sb.append(i);
        }       
        return sb.toString();
    }
}

性能参数比较:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值