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

时间:2022-03-14 10:32:27

约定(基于JDK1.6)

1. String字符串优化

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

String对象的特点

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

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

不变性
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

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

类的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));
}

内存模型分析
java性能优化读书笔记之三《程序优化===字符串优化》

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

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

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

性能参数比较:
java性能优化读书笔记之三《程序优化===字符串优化》