一、字符串拼接

重点注意:对象

特别是String对象的使用,出现字符串拼接时应该使用StringBuilder/StringBuffer代替。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。

字符串的拼接在开发过程中使用是非常频繁的,常用的方式有三种:

(1)、+ 号拼接: str+"456"

(2)、StringBuilder 拼接

(3)、StringBuffer拼接

StringBuffer是保证线程安全的,效率是比较低的,我们更多的是使用场景是不会涉及到线程安全的问题的,所以更多的时候会选择StringBuilder,效率会高一些

那么,问题来了,StringBuilder和“+”号拼接,哪个效率高呢?接下来我们通过字节码的方式进行探究。

public class Test3 {
    public static void main(String[] args) {
        new Test3().m1();
        new Test3().m2();
    }
    public void m1(){
        String s1 = "123";
        String s2 = "456";
        String s3 = s1 + s2;
        System.out.println(s3);
    }
    public void m2(){
        String s1 = "123";
        String s2 = "456";
        StringBuilder sb = new StringBuilder();
        sb.append(s1);
        sb.append(s2);
        String s3 = sb.toString();
        System.out.println(s3);
    }
}

查看 Test3.class的字节码,从解字节码中可以看出, m1()方法源码中是使用+号拼接,但是在字节码中也被编译成了StringBuilder方式

所以,可以得出结论,字符串拼接,+号和StringBuilder是相等的,效率一样。

接下来,我们看一个案例:

public class Test4 {
    public static void main(String[] args) {
        new Test4().m1();
        new Test4().m2();
    }
    public void m1(){
        String str = "";
        for (int i = 0; i < 5; i++) {
            str = str + i;
        }
        System.out.println(str);
    }
    public void m2(){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 5; i++) {
            sb.append(i);
        }
        System.out.println(sb.toString());
    }
}

m1() 与 m2() 哪个方法的效率高?

m1()方法中的循环体内,每一次循环都会创建StringBuilder对象,效率低于m2()方法。当用+号进行一次字符串拼接的时候都会new一个StringBuilder,循环5次就会new 5个StringBuilder对象。m2()方法中在循环体内并没有new StringBuilder对象,而是每次都是调用append方法,即在循环体外new了一次,在循环体内一直append,而new对象和销毁对象(垃圾回收)都会占用系统资源。

总结:for循环中字符串拼接用StringBuilder

二、StringJoiner

1、StringJoiner入门

StringJoiner是Java 8新增的一个API,他是基于StringBuilder实现,用于实现对字符串之间通过分隔符拼接的场景。

有些字符串拼接场景,使用StringBuilder或StringBuffer会显得比较繁琐。

这种字符串有前缀后缀并且由 “,” 分隔的字符串,在 Java 8 之前要使用 StringBuilder/ StringBuffer 进行拼接,如下:

        StringBuilder sb = new StringBuilder();
        sb.append("(");
        sb.append("hello");
        sb.append(",");
        sb.append("guys");
        sb.append(",");
        sb.append("欢迎大家");
        sb.append(")");
        String str = sb.toString();

上述代码显得十分难看又略显繁琐。

Java 8使用StringJonier来实现,如下:

        StringJoiner stringJoiner = new StringJoiner(",", "(", ")");
        stringJoiner.add("hello");
        stringJoiner.add("guys");
        stringJoiner.add("欢迎大家");

StringJonier实现就显得比较优雅!

2、StringJoiner详解

1)、成员变量

prefix:拼接后的字符串前缀
delimiter:拼接时的字符串分隔符
suffix:拼接后的字符串后缀
value:拼接后的值
emptyValue:空值的情况,value为 null 时返回

构造方法:

StringJoiner有两个构造方法

一个是要求传入分隔符、前缀和后缀

public StringJoiner(CharSequence delimiter,
                        CharSequence prefix,
                        CharSequence suffix) {
        Objects.requireNonNull(prefix, "The prefix must not be null");
        Objects.requireNonNull(delimiter, "The delimiter must not be null");
        Objects.requireNonNull(suffix, "The suffix must not be null");
        // make defensive copies of arguments
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        this.emptyValue = this.prefix + this.suffix;
    }

另一个是只需要传入分隔符(前缀和后缀默认是"")

public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
    }

方法:

setEmptyValue:设置空值
toString:转换成 String
add:添加字符串
merge:从另一个 StringJoiner 合并
length:长度(包括前缀后缀)

主要看add()源码:

public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }
    
     private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

可以看到内部其实就是用的StringBuilder进行封装的,首次创建会先拼接前缀,后续先添加分隔符,再添加字符串。

toString()源码

public String toString() {
        if (value == null) {
            return emptyValue;
        } else {
            if (suffix.equals("")) {
                return value.toString();
            } else {
                int initialLength = value.length();
                String result = value.append(suffix).toString();
                // reset value to pre-append initialLength
                value.setLength(initialLength);
                return result;
            }
        }
    }

可以看到内部其实就是用的StringBuilder进行封装的,首次创建会先拼接前缀,后续先添加分隔符,再添加字符串。

主要就通过上述两个方法,实现前缀、后缀以及分隔符的拼接。(最后输出才进行后缀的拼接)

另外一点,add 方法就是返回 StringJoiner 本身,所以可以像StringBuilder/ StringBuffer 一样进行流式处理。如下:

StringJoiner stringJoiner = new StringJoiner(",", "(", ")").add("hello").add("guys").add("欢迎大家");
System.out.println(stringJoiner);

2)、空值处理

没有拼接任何字符串的几个空值处理场景。

 

输出空白字符串

 StringJoiner stringJoiner = new StringJoiner(",");
 System.out.println(stringJoiner.toString());

输出前后缀

StringJoiner stringJoiner = new StringJoiner(",", "[", "]");
System.out.println(stringJoiner.toString());

结果:[]

输出指定字符串

StringJoiner stringJoiner = new StringJoiner(",", "[", "]");
stringJoiner.setEmptyValue("likelong");
System.out.println(stringJoiner.toString());

结果:likelong

 

posted on 2022-01-17 22:06  周文豪  阅读(235)  评论(0编辑  收藏  举报