1.Java程序优化-字符串优化处理

本文探讨了Java中的String对象特性,包括不变性、常量池优化和final定义。深入分析了substring()方法可能导致的内存问题,并介绍了字符串分割的多种方式,如原始的split()方法和更高效的StringTokenizer类。还提到了charAt()方法在字符串处理中的高效应用。

String对象及其特点

       String并不是Java的基本数据类型,从String的代码实现来说,它主要包括三个部分:char数组,偏移量和String的长度。char数组表示String的内容,它是String对象所表示的字符串的超集。String的真实内容还需要由偏移量和长度在这个char数组中进行定位和截取。
       String对象具有三个基本特点:
       不变性:String对象一旦生成,就不能修改它了。String的这个特性可以泛化为不变模式(一种设计模式),即一个对象的状态在被创建后就不再发生变化。不变模式的主要作用在于当一个对象需要被多线程共享,并且访问频繁时,可以省略同步和锁等待时间,从而大幅度提高系统性能。
       针对常量池的优化:当两个String对象拥有相同的值时,它们只引用常量池中的同一个拷贝。当同一个字符串反复出现时,这个技术可以大幅度节省内存空间。

String str1="abc";
String str2="abc";
String str3=new String("abc");
System.out.println(str1==str2);          //返回true
System.out.println(str1==str3);          //返回false
System.out.println(str1==str3.intern()); //返回true,使用intern()方法返回该String对象在常量池中的引用

       str1和str2引用了相同的地址,但是str3却重新开辟了一块内存空间。但即便如此,str3在常量池中的位置和str1是一样的,也就是说,虽然str3单独占用了空间,但是它所指向的实体却和str1完全一样。

       类的final定义:作为final类的String不会有任何子类,这是对系统安全性的保护。同时,对于JDK1.5之前的环境中,使用final定义,有助于帮助虚拟机寻找机会,内联所有的final方法,从而提高系统效率。但这种优化方法在JDK1.5之后,效果并不明显。

subString()方法的内存泄漏

截取子字符串是非常常见的操作之一,String提供了两种截取子字符串的方法:

public String subString(int beginIndex);
public String subString(int beginIndex,int endIndex); //从beginIndex开始,到endIndex为止的子字符串;这个方法存在严重的内存泄漏问题
//上述第二个方法的源码实现
public String substring(int beginIndex, int endIndex) {
        if (beginIndex < 0) {
            throw new StringIndexOutOfBoundsException(beginIndex);
        }
        if (endIndex > value.length) {
            throw new StringIndexOutOfBoundsException(endIndex);
        }
        int subLen = endIndex - beginIndex;
        if (subLen < 0) {
            throw new StringIndexOutOfBoundsException(subLen);
        }
        //上面的代码都是为了检查是否越界的,不重要

        //如果要返回的子字符串是字符串本身就直接返回字符串本身
        //如果不是,返回一个新建的String对象
        return ((beginIndex == 0) && (endIndex == value.length)) ? this
                : new String(value, beginIndex, subLen);
}
//subString()方法最后调用的String构造方法
String(int offset,int count,char value[]){
//String原生内容value数组被复制到新的子字符串中,通过偏移量和长度来截取字符串
    this.value = value; 
    this.offset = offset;
    this.count = count;
}
//试想一下,如果原始字符串很大,截取的字符串却很短,
//那么生成后的子字符串包含了原生字符串的所有内容,并占据了相应的内存空间,
//而仅仅通过偏移量和长度来决定实际的取值
//这种做法提高了运算速度却浪费了大量的内存空间
//这是新版本的JDK中的构造方法,更改了,解决了内存泄漏的问题
public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count <= 0) {
            if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
            }
            if (offset <= value.length) {
                this.value = "".value;
                return;
            }
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        //上面代码部分都是为了检查是否越界的问题
        this.value = Arrays.copyOfRange(value, offset, offset+count);
}

字符串分割和查找

      字符串分割将一个原始字符串,根据某个分割符,切割成一组小字符串。String的split()方法就实现了该方法;

public String[] split(String regex)  //regex可以是一个普通一个正则表达式,从而进行复杂逻辑的字符串分割

       比如字符串“a;b,c:d”,如果要把这些分隔符去掉,只保留字母内容,只需要使用正则表达式“[;|,|:]"。就简单的字符串分割而言,spilt()方法的性能却不太好。

最原始的字符串分割

      直接使用split()方法对字符串进行分割,是最原始的方法。String.split()方法使用简单,功能强大。但是在频繁地使用该方法,性能比较差。

使用效率更高的StringTokenizer类分割字符串

      StringTokenizer是JDK中专门用来处理字符串分割成子串的工具类。

//构造函数
public StringTokenizer(String str,String delim)  //str为要被分割处理的字符串,delim是分割字符
StringTokenizer st = new StringTokenizer(orgStr,";");
for(int i=0;i<10000;i++){
    while(st.hasMoreTokens()){  //知道是否有更多的子字符串需要处理
        st.nextToken();         //得到下一个分割的字符串
    }
    st = new StringTokenizer(orgStr,";"); //这说明使用上述两个方法会改变st对象本身的值,所以需要重新赋值
}

更优化的字符串分割方式

//indexOf()和subString()这两个方法的执行效率非常高,很适合作为高频率的函数使用
String tmp = orgStr;
for(int i=0;i<10000;i++){
    while(true){
        String splitStr=null;
        int j=tmp.indexOf(';');  //找分割符的位置
        if(j<0) break;           //没有分割符存在
        splitStr=tmp.subString(0,j); //找到了分割符,截取前面的子字符串
        tmp=tmp.subString(j+1);      //剩下的需要处理的字符串
    }
    tmp=orgStr;
}

高效率的charAt()方法

//String提供的方法
//返回给定的字符串中,位置在index位置的字符
//功能与indexOf()相反,但效率和indexOf()一样高
public char charAt(int index)

//下面的两个方法的效率远低于charAt()方法

//判断字符串是否以prefix字符串开头
public boolean startWith(String prefix)
//判断字符串是否以suffix结尾
public boolean endWith(String suffix)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值