乐趣区

关于java:关于java中的字符串拼接方式

前置常识

  1. String 是 java 中的不可变类,一旦被实例化就无奈再批改

    不可变类的实例一旦创立,其成员变量的值就不能被批改。这样设计能够缓存 hashcode、应用更加便当以及更加平安等。

  2. java 不反对运算符重载

    运算符重载 :在计算机程序设计中,运算符重载(英语:operator overloading)是多态的一种。运算符重载,就是对已有的运算符从新进行定义,赋予其另一种性能,以适应不同的数据类型。
    语法糖:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁创造的一个术语,指计算机语言中增加的某种语法,这种语法对语言的性能没有影响,然而更不便程序员应用。语法糖让程序更加简洁,有更高的可读性。


常见的字符串拼接办法有 应用符号‘+’拼接、应用 String 类中的 concat 办法拼接、应用 StringBuffer 拼接、应用 StringBuilder 拼接、StringUtils.join

应用符号‘+’拼接

应用 + 拼接字符串,其实只是 Java 提供的一个语法糖,其实现原理是 StringBuilder.append

// 应用符号‘+’拼接字符串
String hollis = wechat + "," + introduce;

// 下面代码的反编译后果
String hollis = (new StringBuilder()).append(wechat).append(",").append(introduce).toString();

从反编译后的代码,应用 + 拼接字符串每次都是 new 了一个 StringBuilder,而后再把 String 转成 StringBuilder,再进行 append。

如果在 for 循环中应用 + 拼接字符串,会频繁的 new 一个对象,不仅仅会消耗工夫,还会造成内存资源的节约。

所以,依据阿里巴巴 Java 开发手册倡议:循环体内,字符串的连贯形式,应用 StringBuilder 的 append 办法进行扩大,而不要应用 +。

应用 String 类中的 concat 办法拼接

用法

String hollis = "wechat".concat(",").concat("introduce");

String 类中 concat 办法的源码

public String concat(String str) {if (str.isEmpty()) {return this;}
    int len = value.length;
    int otherLen = str.length();
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}

能够看到,concat 办法首先创立了一个字符数组,长度是已有字符串和待拼接字符串的长度之和,再把两个字符串的值复制到新的字符数组中,并应用这个字符数组创立一个新的 String 对象并返回。

通过源码咱们也能够看到,通过 concat 办法,其实是 new 了一个新的 String,这也就响应到后面咱们说的字符串的不变性问题上了。

应用 StringBuffer 和 StringBuilder 拼接

StringBuffer 和 StringBuilder 都继承了 AbstractStringBuilder 类,在 AbstractStringBuilder 类中定义了一个字符数组 char[] value ,与 String 类中不同的是,它没有final 修饰符,所以是能够被批改的
StringBuffer 和 StringBuilder 最大的区别就是 StringBuffer 是线程平安的,StringBuffer 应用synchronized 进行申明,重写了 AbstractStringBuilder 类中的局部办法,

@Override
    public synchronized int length() {return count;}

StringUtils.join

StringUtils.join的源码

public static String join(Collection var0, String var1) {StringBuffer var2 = new StringBuffer();

    for(Iterator var3 = var0.iterator(); var3.hasNext(); var2.append((String)var3.next())) {if (var2.length() != 0) {var2.append(var1);
        }
    }

    return var2.toString();}

能够看到,StringUtils.join是通过 StringBuffer 实现的,其最次要的性能是 将数组或汇合以某拼接符拼接到一起造成新的字符串

StringJoiner

StringJoiner 是 java.util 包中的一个类,用于结构一个由分隔符分隔的字符序列(可选),并且能够从提供的前缀开始并以提供的后缀结尾。

用法

public class StringJoinerTest {public static void main(String[] args) {StringJoiner sj = new StringJoiner("Hollis");    // Hollis 是分隔符

        sj.add("hollischuang");
        sj.add("Java 干货");
        System.out.println(sj.toString());

        StringJoiner sj1 = new StringJoiner(":","[","]");    // StringJoiner(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

        sj1.add("Hollis").add("hollischuang").add("Java 干货");
        System.out.println(sj1.toString());
    }
}

// 以上代码返回后果
// hollischuangHollisJava 干货
// [Hollis:hollischuang:Java 干货]

须要留神的是,当咱们 StringJoiner(CharSequence delimiter) 初始化一个 StringJoiner 的时候,这个 delimiter 其实是分隔符,并不是可变字符串的初始值。

依据 StringJoiner.add 办法的源码,能够看到,其实现原理也是依赖的 StringBuilder

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

list.stream().collect(Collectors.joining(","))也是借助 StringJoiner 类实现的列表拼接字符串,然而应用 StringJoiner 类能够不便地减少前缀和后缀,实用于字符串拼接有前、后缀的场景。


如果日常开发中,须要进行字符串拼接,如何抉择?

  • 如果只是简略的字符串拼接,不是在循环体中进行字符串拼接的话,间接应用 + 就好了
  • 如果是在 for 循环中进行字符串拼接,思考应用 StringBuilder 和 StringBuffer
  • 如果在并发场景中进行字符串拼接的话,要应用 StringBuffer 来代替 StringBuilder
  • 如果是通过一个 List 进行字符串拼接,则思考应用 StringUtils.join 和 StringJoiner

参考文章

对于 Java 字符串拼接的几种形式以及性能比拟

退出移动版