约定(基于JDK1.6)
1. String字符串优化
String对象是java重要的数据类型。它不属于java基础类型。通过查看jdk源码分析,可以得到String是由字符数组、偏移量、字符串长度组成。
String对象的特点
- 不变性
- 针对常量的优化
- 类的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();
}
}
性能参数比较: