字符串拼接的多种形式
1. 起因:在公司的时候,自研的框架会应用到字符串的拼接,所以在这里想将多种拼接字符串的形式进行一个小的总结
2. 形式1:应用+号拼接(最不倡议应用)
①不倡议用+号的起因:String底层是常量char数组,具体不可变性,在jvm中是通过字符串常量池来进行存储的。因为底层对加号应用了运算符的重载(c++内容),他在每次拼接的时候都会创立StringBuilder对象,通过StringBuilder对象的append(Stirng str)进行拼接,再通过new String(StringBuilder)来创立String对象。然而这样频繁的创建对象是很耗费性能的,因而不举荐应用+号进行拼接操作。
3. 形式2:应用concat函数进行拼接
①代码举例
String strA = "Hello";
String strB = "world";
String concat = strA.concat(",").concat(strB);
System.out.println(concat);
//后果为hello,world
②底层源码调用函数图
③底层源码解析之横向调用(复制原String的值)
//String中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);
//复制原来的String到新的buf数组中
-------------------------------------下面的是横向,上面是纵向
//复制增加的str到新的buf数组中
str.getChars(buf, len);
return new String(buf, true);
}
//在concat函数中调用了Arrays中copyOf函数
//将源数组全副复制到从索引0开始的copy数组中
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
④底层源码解析之System.arraycopy(对数组进行管制,比copyOf更加灵便)
-
一维数组测试System.arraycopy是否为同一存储空间
char[] one_char1 = new char[]{'a','b','c'}; char[] one_char2 = new char[5]; //举例:将one_char1中从索引1到索引2(长度为2)的值复制到one_char2的索引为3和索引为4的中央 //被复制数组;源数组被复制的内容的结尾索引; //复制数组;复制数组搁置被复制的内容的结尾索引;被复制的内容的索引个数 //留神:①其中三个数字参数不能为正数;②运行时容易产生数组越界 System.arraycopy(one_char1,1,one_char2,3,2); for(char value:one_char2){ System.out.println("value2="+value); System.out.println(); } //后果为空,空,空,b,c //测试批改one_char[2]的值是否会批改one_char[1]的值 one_char2[3] = 't'; for(char value:one_char1){ System.out.println("value1="+value); } //one_char1后果仍然为a,b,c; //可见one_char[1]这里并不会随one_char2批改而被批改,阐明两个数组不是同一个存储地址,也就是咱们常说的浅拷贝,或者说值传递
-
二维数组测试System.arraycopy是否为同一存储空间
char[][] two_char1 = new char[][]{{'a','b','c'},{'e','f','g'},{'h','i','j'}}; char[][] two_char2 = new char[3][5]; //举例:将two_char1中从第一行到第二行(行数为2)的值复制到two_char2的第一行中;如果该行不够,则存储至下一行 //被复制数组;源数组被复制的内容的行数索引; //复制数组;复制数组搁置被复制的内容的行数索引;被复制的内容的行数个数 //留神:①其中三个数字参数不能为正数;②运行时容易产生数组越界 System.arraycopy(two_char1,0,two_char2,1,2); for(char[] one_Array:two_char2){ for(char value:one_Array){ System.out.println("value2="+value); } two_char2[0][0] = 't'; for(char[] one_Array:two_char1){ for(char value:one_Array){ System.out.println("value2="+value); } } //批改two_char2的值,two_char1的值也随之而扭转 //two_char1的值被扭转,two_char2的值也绝对应被扭转,阐明他们是雷同的存储空间,也就是咱们所说的深度拷贝,或者说地址传递
⑤底层源码解析之纵向调用(复制concat(xxx)中xxx的值)
//String中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); //调用了上面的getchars函数 return new String(buf, true); } //String中getchars函数 void getChars(char dst[], int dstBegin) { //相似的原理,还是调用了下面的本地函数System.arraycopy System.arraycopy(value, 0, dst, dstBegin, value.length); }
3.形式3:应用StringBuilder进行拼接
①代码举例
StringBuilder strA = new StringBuilder("hello,"); strA.append("world"); System.out.println(strA); //后果为hello,world
②底层源码调用函数图
③源码解析
public StringBuilder append(String str) { super.append(str); return this; } //AbstractStringBuilder是StringBuilder的父类,由super.append(str)进入该办法 public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); //count示意底层char[]数组中元素的个数 //该办法暂不细说,下回分解 ensureCapacityInternal(count + len); //和下面的getchars一样,调用的是底部的system.arrayCopy函数 str.getChars(0, len, value, count); count += len; return this; }
4.形式4:应用StringBuffer拼接
①区别:应用StringBuffer拼接来说,和StringBuilder相似,他都是调用他们的形象父类AbstractStringBuilder中的办法,只是StringBuffer上的办法都应用了synchronized关键字罢了
②应用场景:要求线程平安的状况下应用
5.形式5:应用StringJoiner来进行拼接(须要同一种分隔符进行屡次拼接时)
①应用举例1(不带前后缀)
StringJoiner sj = new StringJoiner(",","[","]"); sj.add("hello").add("world").add("yangshengyuan"); //后果为hello,world,yangshengyuan
②应用举例2(带前后缀)
StringJoiner sj = new StringJoiner(",","[","]"); sj.add("hello").add("world").add("yangshengyuan"); //后果为[hello,world,yangshengyuan]
③源码解析
//StringJoiner中add办法 public StringJoiner add(CharSequence newElement) { prepareBuilder().append(newElement); return this; } //StringJoiner中prepareBuilder办法 private StringBuilder prepareBuilder() { //value是StringBuilder类型的,用于存储整个的数据,也就是StringJoiner的add办法底层就是用StringBuilder的append办法来实现的 //如果value为空,则增加为前缀;反之则增加分隔符 if (value != null) { value.append(delimiter); } else { value = new StringBuilder().append(prefix); } return value; }
6.引发写这篇文章的代码(通过反射先类中任意属性都能通过分隔符进行拼接)
//因为整个我的项目是用ORM框架,他须要一种格局进行拼接和输入,心愿可能不要每次模型批改的时候都要批改这个函数,因而在这里应用反射让他可能不论你实体类增加多少属性我都能这样拼接和输入,保障了代码的健壮性吧
public String getString() throws IllegalArgumentException, IllegalAccessException { StringJoiner sj = new StringJoiner(","); Class<ScoreResultmap> srClass = ScoreResultmap.class; Field[] fields = srClass.getDeclaredFields(); for(Field field:fields) { String value = String.valueOf(field.get(this)); sj.add(value); } return sj.toString(); }
7.总结:本文共介绍了4种不同的字符串拼接的办法、应用场景以及他们的源码剖析。对于ensureCapacityInternal办法我将会在下次进行概述,感激大家花工夫观看我的文章。
发表回复