字符串拼接还在用StringBuilder?快试试Java8中的StringJoiner吧,真香!

Java 8 引入的 StringJoiner 类为字符串拼接提供了更为简洁和灵活的方式。相比 StringBuffer 和 StringBuilder,StringJoiner 使用更直观,可以轻松设置分隔符、前缀和后缀。例如,拼接逗号分隔的数字序列,使用 StringJoiner 可简化代码。此外,它还可用于 SQL 拼接等场景,提高代码的可读性和效率。

前言

之前,我们经常会通过StringBuffer或者StingBuilder对字符串进行拼接,但是你知道Java8中推出的StringJoiner吗?它比前者更加优美、灵活,如果你现在还使用StringBuffer拼接,强烈推荐你试试StringJoiner。


介绍

在实用StringJoiner类之前,如果我们想要数据最终的字符串以逗号隔开,大概是这样的

StringBuilder sb = new StringBuilder();
IntStream.range(1,10).forEach(i->{
    sb.append(i+"");
    if( i < 10){
        sb.append(",")
    } 
});

如果引入StringJoiner,如何处理呢?

StringJoiner sj = new StringJoiner(",");
IntStream.range(1,10).forEach(i->sj.add(i+""));

看着是不是更简单直观了呢?

另外,StringJoiner类的构造函数,还可以做到可选择性地从我们自定义的前缀开始和自定义的后缀结尾,比较灵活和实用。

    //值依次是分割符 , 前缀  ,后缀
    StringJoiner stringJoiner = new StringJoiner(",", "[", "]");
    stringJoiner.add("xiao");
    stringJoiner.add("zhi");
    System.out.println(stringJoiner.toString());  

输出结果:[xiao,zhi]

StringJoiner在处理sql拼接上面,也非常方便,如拼接 sql 的in条件的时候:

StringJoiner joiner3 = new StringJoiner("','", "'", "'");
joiner3.add("1").add("2");
//输出 : '1','2'

更多实用的功能,大家可以探索。


源码

这个类的源码很简单,大家很容易就可以看明白。StringJoiner 更像一个装饰者模式,对外隐藏了StringBuilder。

不过需要注意的是 StringJoiner 并且没有处理一些基本的集合元素情况,比如加入列表的元素,更像针对Collectors而设计。

package java.util;

public final class StringJoiner {
    private final String prefix;//前缀
    private final String delimiter;//间隔符
    private final String suffix;//后缀
    private StringBuilder value;//值

    private String emptyValue;//空值

    public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
        //默认前缀和后缀为"",重载调用
    }

    public StringJoiner(CharSequence delimiter,
                        CharSequence prefix,
                        CharSequence suffix) {
        //间隔符,前缀和后缀判断是否为null,null将抛出异常
        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"); 
        // 成员变量赋值
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        this.emptyValue = this.prefix + this.suffix;//空值被设置为只有前后缀
    }
 //设置空值,检查是否为null
    public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }

    @Override
    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后续append字符串
    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }
 //合并StringJoiner,注意后面StringJoiner 的前缀就不要了,后面的appen进来
    public StringJoiner merge(StringJoiner other) {
        Objects.requireNonNull(other);
        if (other.value != null) {
            final int length = other.value.length();
            // lock the length so that we can seize the data to be appended
            // before initiate copying to avoid interference, especially when
            // merge 'this'
            StringBuilder builder = prepareBuilder();
            builder.append(other.value, other.prefix.length(), length);
        }
        return this;
    }
 //初始化,先添加前缀,添加之后每次先添加间隔符
    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

    public int length() {
        // Remember that we never actually append the suffix unless we return
        // the full (present) value or some sub-string or length of it, so that
        // we can add on more if we need to.
        //添加后缀的长度
        return (value != null ? value.length() + suffix.length() :
                emptyValue.length());
    }
}

本文分享自微信公众号 - Java知音 ,作者柏御

Java 中,**拼接少量字符串时通常不建议使用 `StringBuilder`**。因为在这种情况下,Java 编译器已经做了优化,使用 `+` 或者 `+=` 拼接字符串的性能与 `StringBuilder` 几乎相同,甚至更好,因为代码更简洁、可读性更高。 --- ## ✅ 示例:少量拼接(推荐使用 `+`) ```java String result = "Hello, " + name + "! You have " + count + " messages."; ``` ### 为什么可以这样做? - Java 编译器会自动将多个字符串拼接优化为使用 `StringBuilder`。 - 上面这句代码在编译后大致等价于: ```java String result = new StringBuilder() .append("Hello, ") .append(name) .append("! You have ") .append(count) .append(" messages.") .toString(); ``` 所以你写的 `+` 拼接,在编译阶段就已经被优化了。 --- ## 🧠 那么多少是“少量”? 没有明确的定义,但一般认为: - **2~5次拼接操作** 可以算作“少量”; - 如果是在循环中或高频调用的方法中拼接多次,则应考虑使用 `StringBuilder`。 --- ## ❌ 不推荐的写法(即使是少量拼接) ```java String result = ""; result += "A"; result += "B"; result += "C"; ``` 虽然这里拼接次数少,但每次 `+=` 都会产生一个新的 `StringBuilder` 实例(如前面所讲),这种写法在循环外看起来影响不大,但仍不推荐。 --- ## ✅ 推荐做法总结 | 场景 | 推荐方式 | |------------------------------|----------------------| | 单行简单拼接(如日志、提示) | 使用 `+` | | 循环内或频繁拼接 | 使用 `StringBuilder` | | 多线程环境下拼接 | 使用 `StringBuffer` | | 构建复杂字符串结构 | 使用 `StringBuilder` | --- ## 🔍 性能对比(示例) | 拼接方式 | 耗时(100次以内) | |----------------|--------------------| | `+` | ~0.01ms | | `StringBuilder`| ~0.01ms | 两者差别极小,但在代码简洁性和可读性上,`+` 更优。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值