java五种拼接字符串的方法分析(腾讯面试题)

今天在腾讯面视的时候有被问到java中有哪几种拼接字符串的方法。

回顾起来,这应该是一个很重要的点,可惜自己没答好。

回来后仔细分析String内里面的源码,对于拼接字符串进行一个分析。

 

 

1.最简单使用,最常见的 “+”方法。

 ”+“ 方法源码中描述如下

The Java language provides special support 
for the string concatenation operator ( + ), 
and for conversion of  other objects to strings. 
String concatenation is implemented  through 
the StringBuilder(or StringBuffer) class and its append method.

意思就是:String本身是不变的对象,但是string的+号操作符是通过StringBuilder或StringBuffer(可以通过反汇编class文件,看到使用的StringBuilder来实现的。)

 “+” 是在java内部是对+进行了重载,在处理String的过程中要创建一个StringBuffer对象,用StringBuffer对象的append方法对字符串进行连接,最后调用toString方法返回String字符串。

所以 “+” 方法可以随意使用,比如String+int, String+Double····但是效率较低。

 

 

 

 

 

2.较好的“concat”方法

看看String类里面concat源码。

 

 

    private final char value[];    

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

1.首先传入一个字符串str,然后得到str的长度,命名为otherLen。

2.判断oherLen是否为0。如果长度为0,那么返回原字符串。(一个字符串加上一个空字符串,得到还是原字符串)

3.取原字符串长度,命名为len。(注意这里取是从value数组里面取,value数组里实际上就是存储的原字符串)

4.然后用Arrays.copyOf函数,复制原数组,并且把数组长度从len拓展到len+otherLen。

5.用getChars函数,把str复制进入buf数组里,从下标为len的地方开始复制。

6.用buf新构建一个String对象,并且返回。

ps:注意,如果传入的str为空,那么就不会构建新String对象,而是直接返回原对象。并且concat只能对字符串使用。因为已经确认是字符串型,所以没有类型转换,效率相对 “+” 高许多。

3.StringUtils的join方法

 

join方法首先需要导入一个包:

org.apache.commons.lang

导入方法自行Google。

jar包下载
http://maven.outofmemory.cn/org.apache.commons/commons-lang3/3.3.2/

所以严格来说这个不是String类里面自带的一个方法。但是也值得一提。(JDK8里面String类也有了join方法,效果类似,传参不同而已)

在StringUtils.java里面join的源码如下

    public static String join(final Iterable iterable, final String separator) {
        if (iterable == null) {
            return null;
        }
        return join(iterable.iterator(), separator);
    }

里面用迭代器方法遍历整个传进来的数组或集合,然后用第二个参数separator来把数组或集合连接起来。(关于迭代器方面,了解的不深,不过根据代码运行结果可以逆推出结论)。

那么join方法具体思路大概就清楚了:

1.申明一个类的列表List。

2.用list.add()方法,把要连接的字符串存入list里面。

3.用StringUtils.join(list,""),方法把list里面的字符串连接起来。(因为第二个参数是“”,所以list里面数据就直接相连,相当于字符串就连接起来了)

这个方法速度快在list的add方法比String类里面的+或者concat都迅速。

 

 

4.用StringBuffer里面append方法

    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

 调用父类AbstractStringBuilder里面的append方法。

父类里面方法如下:

 

    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

如果传进来的字符str为null的话,调用appendNull方法(见下面代码),那么直接在后面加上null这个字符串。(比如已经有"abc"了,有一个字符串a=null,调用append(a)方法,就会进入appendNull,最后得到的结果是“abcnull”,而不是"abc"。但是如果加入进来的a="",那么结果就是"abc"。)

如果不是null的话,那么就开扩容量,又用getChars方法,把str给放入value里面(value里面是原字符串对应的数组)去,并且把对于长度修正。

 

    private AbstractStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

 

 

5.用StringBuilder里面append方法

从上面就可以发现,StringBuffer其实也是调用了AbstractStringBulider里面的方法。

StringBuilder也是如此,只不过比StringBuffer少了一个toStringCache以及一些方法的synchronized关键字。这些关键字主要是用来确保线程的安全问题。所以StringBuffer比StringBuilder虽然看起来一样,但是牺牲了少量的性能,换取了一定的安全性。

下面是StringBuilder里面append源码:

    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

里面调用的super.append是一模一样的跳入AbstractStringBuilder里面的append方法。

 

整个实现和测试的代码如下:

import java.sql.Time;
import java.util.*;
import org.apache.commons.lang3.StringUtils;

public class test {
    public static void main(String agv[]) throws InterruptedException {
        String strX="x";
        String strSum="";
        //方法1,+法
        long start = System.currentTimeMillis();
        for(int i=0;i<120000;i++){
            strSum+=strX;
        }
        long end = System.currentTimeMillis();

        System.out.println(end-start);
        System.out.println(strSum);
      //  System.out.println(strSum.length());

       // Thread.sleep(100);
        strSum="";
        //方法2,concat
        start = System.currentTimeMillis();
        for(int i=0;i<120000;i++){
            strSum=strSum.concat(strX);
        }
        end = System.currentTimeMillis();
        System.out.println(end-start);
        System.out.println(strSum);
       // System.out.println(strSum.length());
      //  Thread.sleep(100);

        strSum="";
        //方法3,join
        List list=new ArrayList();
        start=System.currentTimeMillis();
        for(int i=0;i<120000;i++){
            list.add(strX);
        }
        strSum=StringUtils.join(list, "");

        end = System.currentTimeMillis();
        System.out.println(end-start);
        System.out.println(strSum);//输出list看是否正确
      //  System.out.println(list.size());
       // Thread.sleep(100);
        strSum="";
        //方法4,StringBuffer
        StringBuffer sbr=new StringBuffer();
        start=System.currentTimeMillis();
        for(int i=0;i<120000;i++){
            sbr.append(strX);
        }
        strSum=sbr.toString();
        end=System.currentTimeMillis();
        System.out.println(end-start);

        System.out.println(strSum);
        //System.out.println(strSum.length());

        //Thread.sleep(100);
        //方法5,StringBuilder
        StringBuilder sbd=new StringBuilder();
        start=System.currentTimeMillis();
        for(int i=0;i<120000;i++){
            sbd.append(strX);
        }
        strSum=sbd.toString();
        end=System.currentTimeMillis();
        System.out.println(end-start);

        System.out.println(strSum);
        //System.out.println(strSum.length());


        StringBuffer testNull=new StringBuffer();//用于测试null的
        String a="abc";
        testNull.append(a);
        a=testNull.toString();
        System.out.println(a);
        a= null;
        testNull.append(a);
        a=testNull.toString();
        System.out.println(a);
    }
}

运行结果:

java五种拼接字符串的方法分析(腾讯面试题)_第1张图片

 

总结:

 

StringBuilder>StringBuffer>join>concat>"+"

 

但是需要注意的是,StringBuilder会有线程不安全问题(原因在上文),而StringBuufer不会。

所以在大量字符串处理时,尽可能的多使用StringBuffer或者join。对于少量的字符串处理,可以使用+或者concat。

并且,因为String类型是常量,而StringBuffer,StringBuilder类型是变量,所以JVM在对其处理时,String往往会多次被销毁,创建,这会占据大量的时间。而另外两种相对较少。

 

下面是放在Github的代码以及本人的Github,不定期的更新一些Java学习的代码以及有意思的面试题对应的代码。

https://github.com/2017faker/my_project.git

https://github.com/2017faker

你可能感兴趣的:(Java,面视)