String,StringBuffer以及StringBuilder详解
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
本篇的个人理解也是站在巨人的肩膀上进行了个人的总结
一.基础了解篇
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
–>1.1 代码编写
—->1.1.1实际代码
public class String_StringBuilder {
public static void main(String[] args) {
int testTime=10000;
long timeString = stringAddText(testTime);
System.out.println("String使用拼接消耗时间------------->"+timeString);
long timeStringBuilder = stringBuilderAppendTest(testTime);
System.out.println("StringBuilder使用append时间---->"+timeStringBuilder);
long timeStringBuiffer= stringBufferAppendTest(testTime);
System.out.println("StringBuffer使用append时间------>"+timeStringBuiffer);
}
//通过"+"直接拼写
public static long stringAddText(int count) {
String num = "0123456789";
String num2= "";
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
num2+=num;
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
//通过StringBuilder拼写
public static long stringBuilderAppendTest(int count) {
String num = "0123456789";
StringBuilder num2 = new StringBuilder();
// StringBuilder num3 = new StringBuilder("0123456789");
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
num2.append(num);
// num3.append(num3); 此处和上面打开会异常
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
//通过StringBuffer拼写
public static long stringBufferAppendTest(int count) {
String num = "0123456789";
StringBuffer num2 = new StringBuffer();
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
num2.append(num);
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
- - - - - - - - - - - - - - - - - - - - - - -
public class String_StringBuilder {
public static void main(String[] args) {
int testTime=10000;
long timeString = stringAddText(testTime);
System.out.println("String使用拼接消耗时间------------->"+timeString);
long timeStringBuilder = stringBuilderAppendTest(testTime);
System.out.println("StringBuilder使用append时间---->"+timeStringBuilder);
long timeStringBuiffer= stringBufferAppendTest(testTime);
System.out.println("StringBuffer使用append时间------>"+timeStringBuiffer);
}
//通过"+"直接拼写
public static long stringAddText(int count) {
String num = "0123456789";
String num2= "";
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
num2+=num;
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
//通过StringBuilder拼写
public static long stringBuilderAppendTest(int count) {
String num = "0123456789";
StringBuilder num2 = new StringBuilder();
// StringBuilder num3 = new StringBuilder("0123456789");
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
num2.append(num);
// num3.append(num3); 此处和上面打开会异常
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
//通过StringBuffer拼写
public static long stringBufferAppendTest(int count) {
String num = "0123456789";
StringBuffer num2 = new StringBuffer();
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
num2.append(num);
}
long endTime = System.currentTimeMillis();
return endTime - startTime;
}
—->1.1.2代码结果
- 测试前提:使用老i5机型4G内存电脑测试
- 测试内容:都对"0123456789"进行拼写测试
- testTime=10000;
String使用+号拼接消耗时间------------->623ms (600-630ms)
StringBuilder使用append时间----------->1ms (0-1ms)
StringBuffer使用append时间------------>1ms (0-1ms)
- testTime=20000;
String使用+号拼接消耗时间------------->2687ms (2680-2689ms)
StringBuilder使用append时间----------->1ms (1ms)
StringBuffer使用append时间------------>2ms (2ms)
- testTime=30000;
String使用+号拼接消耗时间------------->6361ms (6360-6420ms)
StringBuilder使用append时间----------->2ms (1-2ms)
StringBuffer使用append时间------------>3ms (2-3ms)
- 测试结果:
- 从结果可以看出随着循环次数的增加三者的性能区别也较为明显的体验了出来(StringBuilder>StringBuffer>>>String)
- 通过"+"拼写和用append进行拼写的性能差距非常之巨大
- 所以我们要用字符串缓冲区(StringBuffer和StringBuilder)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
–>1.2 字符串
—->1.2.1什么是字符串
- Java字符串类(java.lang.String)是java中使用最多的类,也是最为特殊的类
- 本质:是字符数组char[],并且由于被final修饰其值不可改变private final char value[];
—->1.2.2字符串特点
- String类是final,不可被继承
- String类特殊创建方式:String str="abc";
- "abc"是常量池的字符串对象
- str是栈中值为"abc"地址的该对象引用
- String对象可以通过"+"或"concat()"串联生成新的字符串
—->1.2.3字符串创建
- 使用new关键字创建字符串:String str1 = new String("abc");
- 在栈(stack)中创建 str1指向堆中
- 在堆(heap)中创建新String对象 new String
- 在常量区(static area)找是否有"abc",无则创建
- 直接指定:String str2="abc";
- 在栈(stack)中创建str2指向常量池中
- 在常量区(static area)找是否有"abc",无则创建
- 使用串联生成新的字符串: String str3="ab"+"c"
- 在栈(stack)中创建str2指向常量池中
- 在常量池中对"ab"以及"c"进行拼接为"abc", 找是否有"abc",无则创建
- 原理:
- 不管使用何种方式创建字符串,都会先在常量区的String池中找是否存在相同内容字符串对象
- 只要使用new来创建,就一定会在(堆或栈)创建新的对象
- 使用直接指定或者字符串串联,仅接茬维护String池中的字符串
- 使用包含变量的表达式来创建String对象,则不仅检查String池,还会在堆栈区创建String对象.
—->1.2.4字符串"+"
- 字符串拼接理解如下:
- 源码:
//concat()源码
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
- 字符串的拼接其实就是使用了concat();实现过程如下:
- 获取要拼写的字符串,并获取其长度otherLen
- 若长度为0则直接返回原字符串
- 如果长度不为0,则获取原有字符串长度len
- 开辟一个长度为len+otherLen的新字符数组并将字符串内容存放进去
- 返回新字符串
- 源码:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
–>1.3 字符串缓冲区(StringBuffer和StringBuilder)
—->1.3.1认识字符串缓冲区
- 本质:一个长度可变的容器
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
—->1.3.2字符串缓冲区特点
- 默认长度(16个字符)
public StringBuffer() {
super(16);
}
- 使用字符串缓冲区处理字符串时不会生成新的对象,在内存使用要优于String类
- 对StringBuffer对象每次修改都会改变对象自身,与String类最大区别
—->1.3.3字符串缓冲区创建
- 创建
//创建空对象
StringBuffer s = new StringBuffer();
//创建带有内容对象
StringBuffer s = new StringBuffer(“abc”);
- String与StringBuffer转换
- 直接转换
StringBuffer s = “abc”; //赋值类型不匹配
StringBuffer s = (StringBuffer)”abc”; //不存在继承关系,无法进行强转
- 实际转换代码:
String s = “abc”;
StringBuffer sb1 = new StringBuffer(“123”);
StringBuffer sb2 = new StringBuffer(s); //String转换为StringBuffer
String s1 = sb1.toString(); //StringBuffer转换为String
—->1.3.4字符串缓冲区append()方法
- append()方法会判断增加的字符串或字符串缓冲区对象长度,如果长度超过现有长度则扩容
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
- 扩容源码( ensureCapacityInternal())
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code 传入现在使用长度+添加字符串长度,若超出初始容器大小(溢出)则扩容
if (minimumCapacity - value.length > 0)
expandCapacity(minimumCapacity);
}
/**
* This implements the expansion semantics of ensureCapacity with no
* size check or synchronization.
*/
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2;//扩容为上次大小的两倍+2字节
if (newCapacity - minimumCapacity < 0)
newCapacity = minimumCapacity;
if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow
throw new OutOfMemoryError();
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
–>1.4简单总结
—->1.4.1字符串缓冲区为何在增删改时性能大大优于String类型
- String类型在增删改时会先创建新的容器,随后将原始内容和添加内容一起放入新的容器中去(原始内容不发生改变)
- 字符创缓冲区在增删改时会直接在本身容器上进行添加,如果容器大小不足,则扩容,并不会创建新的对象,所以性能极高
—->1.4.2字符串缓冲区StringBuffer和StringBuilder区别
- StringBuffer是线程安全的
- StringBuilder是线程不安全的,但是性能相比前者稍高(一般开发中用).