0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Java 8新增的 StringJoiner用法

科技绿洲 来源:Java技术指北 作者:Java技术指北 2023-10-11 15:40 次阅读

概述

StringJoiner is used to construct a sequence of characters separated by a delimiter and optionally starting with a supplied prefix and ending with a supplied suffix.

Prior to adding something to the StringJoiner, its sj.toString() method will, by default, return prefix + suffix. However, if the setEmptyValue method is called, the emptyValue supplied will be returned instead. This can be used, for example, when creating a string using set notation to indicate an empty set, i.e. "{}", where the prefix is "{", the suffix is "}" and nothing has been added to the StringJoiner.

StringJoiner 源码的定义可以看出,它是 java.util 包中的一个类,被用来构造一个由分隔符分隔的字符串,并且可以从提供的前缀字符串开头,以提供的后缀字符串结尾。

通常我们拼接字符串都是使用 StringBuilder 或者 StringBuffer 来实现的。这个时候,我们可能就会有一个疑问了, StringJoiner 的价值是什么?到底为什么要到这个时候创造它。

源码解析

好,我们先看一下 StringJoiner 的构造函数, StringJoiner 一共有 2 个构造函数。构造函数很简单,没有什么可以多讲的,就是对 分隔符、前缀和后缀字符串的初始化。

public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
    }
    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();
    }

另外 StringJoiner 有 5 个公有方法,其中比较常用的就是 addtoString 。我们也来看看这两个常用方法。

public final class StringJoiner {
    private String[] elts;
    
    @Override
    public String toString() {
        final String[] elts = this.elts;
        if (elts == null && emptyValue != null) {
            return emptyValue;
        }
        final int size = this.size;
        final int addLen = prefix.length() + suffix.length();
        if (addLen == 0) {
            compactElts();
            return size == 0 ? "" : elts[0];
        }
        final String delimiter = this.delimiter;
        final char[] chars = new char[len + addLen];
        int k = getChars(prefix, chars, 0);
        if (size > 0) {
            k += getChars(elts[0], chars, k);
            for (int i = 1; i < size; i++) {
                k += getChars(delimiter, chars, k);
                k += getChars(elts[i], chars, k);
            }
        }
        k += getChars(suffix, chars, k);
        return new String(chars);
    }
    public StringJoiner add(CharSequence newElement) {
        final String elt = String.valueOf(newElement);
        if (elts == null) {
            elts = new String[8];
        } else {
            if (size == elts.length)
                elts = Arrays.copyOf(elts, 2 * size);
            len += delimiter.length();
        }
        len += elt.length();
        elts[size++] = elt;
        return this;
    }
}

我们来看下 add 方法的实现,看起来也挺简单的,就是把待拼接的字符串,放到一个字符串数组里面。toString() 方法的时候,才是真正做字符串拼接的过程。我例子中的代码是 JDK 11, 相比 JDK 8 中,StringJoiner 是通过 StringBuilder 来实现的。

既然 JDK 8 的时候,已经使用了StringBuilder 来实现,那么为什么还要改成 String[] 来缓存所有的待拼接的字符串。这个就要涉及到JVM底层的优化,我们这里暂时不展开讲这个问题了。

前面已经提过既然已经有了 StringBuilder,为什么还要造一个StringJoiner,它的优势到底在哪里,我们接着来找找原因。很快我们在代码类的注释中找到了猫腻,在注释中标记了Collectors#joining

A StringJoiner may be employed to create formatted output from a java.util.stream.Stream using java.util.stream.Collectors.joining(CharSequence).

* @see java.util.stream.Collectors#joining(CharSequence)
 * @see java.util.stream.Collectors#joining(CharSequence, CharSequence, CharSequence)

那我们就顺藤摸瓜,看看 Collectors#joining 有什么跟 StringJoiner 有关联的呢?

public static Collector< CharSequence, ?, String > joining() {
        return new CollectorImpl< CharSequence, StringBuilder, String >(
                StringBuilder::new, StringBuilder::append,
                (r1, r2) - > { r1.append(r2); return r1; },
                StringBuilder::toString, CH_NOID);
    }
    public static Collector< CharSequence, ?, String > joining(CharSequence delimiter) {
        return joining(delimiter, "", "");
    }
    public static Collector< CharSequence, ?, String > joining(CharSequence delimiter,
                                                             CharSequence prefix,
                                                             CharSequence suffix) {
        return new CollectorImpl<  >(
                () - > new StringJoiner(delimiter, prefix, suffix),
                StringJoiner::add, StringJoiner::merge,
                StringJoiner::toString, CH_NOID);
    }

原来啊,Java 8Stream 是借助了 StringJoiner 来实现的。这个时候,我们可能会想,为什么不使用 StringBuilder 来实现呢?我们可以从上面的代码里看出,如果 使用 StringBuilder 来构造拼接的话,在没有前后缀的情况下,应该还是简单的,事实上JDK 官方组织也选择了 StringBuilder 。但是一旦涉及到拼接之类的操作,那如果还是使用 StringBuilder 的话,那就真的是太复杂了。

所以 StringJoinerJava 8 的地位是 StringBuilder 所不能代替的。

总结

本文介绍了 Java 8 开始提供的字符串拼接类 StringJoinerJDK 8StringJoiner 是通过 StringBuilder 实现的, 所以它的性能和 StringBuilder 差不多,它也是非线程安全的。JDK 11 中已经对其进行了优化,通过 String[] 来代理 StringBuilder

在日常的开发过程中,我们怎么选择字符串拼接类呢?

  1. 简单的字符串拼接,直接使用 + 即可。
  2. 在 for 循环之类的场景下需要字符串拼接,可以优先考虑使用 StringBuilder 。
  3. 在使用 Java Stream 的场景下需要字符串拼接,可以优先考虑使用 StringJoiner。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • JAVA
    +关注

    关注

    19

    文章

    2904

    浏览量

    102998
  • 字符串
    +关注

    关注

    1

    文章

    552

    浏览量

    20132
  • 函数
    +关注

    关注

    3

    文章

    3882

    浏览量

    61310
  • string
    +关注

    关注

    0

    文章

    40

    浏览量

    4648
收藏 人收藏

    评论

    相关推荐

    Java多线程的用法

    本文将介绍一下Java多线程的用法。 基础介绍 什么是多线程 指的是在一个进程中同时运行多个线程,每个线程都可以独立执行不同的任务或操作。 与单线程相比,多线程可以提高程序的并发性和响应
    的头像 发表于 09-30 17:07 627次阅读

    java spring教程

    java spring教程理解Spring 实现原理掌握Spring IOC,AOP掌握Spring的基础配置和用法熟练使用SSH开发项目Spring 概述Spring 整体结构Spring实例
    发表于 09-11 11:09

    [转帖]JAVA中级视频在线学习

     7:集合排序    内容包括:讲解和代码示例集合排序的写法和用法,代码示例递归和BigInteger的用法  8:泛型
    发表于 05-19 14:26

    Java中的常用异常处理方法 java推荐

    Java中,异常情况分为Exception(异常)和Error(错误)两大类,Java异常通常是指程序运行过程中出现的非正常情况,如用户输入错误、除数为零、需要处理的文件不存在、数组下标越界等
    发表于 01-19 17:26

    新增16条设计规约!阿里巴巴Java开发手册(详尽版)开放下载!

    摘要:2018年6月,《阿里巴巴Java开发手册》再次刷新代码规范认知,我们新增了16条设计规约!现免费开放下载,不可错过!《阿里巴巴Java开发手册》是阿里内部Java工程师所遵循的
    发表于 06-06 17:24

    PC与PLC通信为什么支持.Net Java C++和Py

    一个PLC通讯框架支持.Net Java C++和Py一个PLC通讯框架支持.Net Java C++和Py的框架信息JAVA用法S7通讯协议modbus通讯协议
    发表于 07-01 09:29

    java并发编程实战之辅助类用法

    二.CyclicBarrier用法 三.Semaphore用法 若有不正之处请多多谅解,并欢迎批评指正。 一.CountDownLatch用法 CountDownLatch类位于java
    发表于 09-27 16:50 0次下载

    java final关键字用法技巧汇总解析

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法
    发表于 12-01 11:02 988次阅读
    <b class='flag-5'>java</b> final关键字<b class='flag-5'>用法</b>技巧汇总解析

    Java数组的常用方法_Java:数组工具类Arrays类的常用方法的用法及代码

    本文主要详细介绍了Java数组的常用方法以及数组工具类Arrays类的常用方法的用法及代码。
    发表于 01-29 10:25 2732次阅读

    如何正确区分Java中super函数用法

    Java中super函数有很多方法,在使用的时候我们应该如何正确区分? 2 方法 三种用法: 访问父类的方法。 调用父类构造方法。 访问父类中的隐藏成员变量。
    的头像 发表于 02-21 15:08 362次阅读

    Java枚举的特点及用法

    Java 枚举出现之前,通常会使用常量类来表示一组固定的常量值,直到Java 1.5之后推出了枚举,那么枚举类型有哪些特点,它比常量类又好在哪里呢。 本文将分析一下枚举的特点及用法。 1.
    的头像 发表于 09-30 10:02 427次阅读

    java 8的日期用法

    java 已经出到 17 了,而小编还在用 8 的版本,在 8 中已经推出了新的日期 API,不在使用 。java.time 包下提供了用于日期、时间、实例和周期的主要 API。 获取今天
    的头像 发表于 10-09 15:50 231次阅读

    this关键字在Java中的用法

    this 关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。 其实简单来说 this 关键字就是表示当前对象,下面我们来具体介绍 this 关键字在Java中的用法。 1、调用成员变量
    的头像 发表于 10-10 16:49 321次阅读
    this关键字在<b class='flag-5'>Java</b>中的<b class='flag-5'>用法</b>

    instanceof在java中的用法

    instanceof是一个Java关键字,用于判断一个对象是否是某个类的实例,或者是其子类的实例。 instanceof的语法是: object instanceof class 其中,object
    的头像 发表于 11-21 10:25 535次阅读

    java中obj类型的实战用法

    类型具有许多实战用法,下面将详细介绍一些常见的使用场景。 作为方法参数和返回值的类型 在Java中,可以使用obj类型作为方法的参数或返回值类型。这种用法可以使方法具有更广泛的适用性,因为obj类型可以接受任何对象作为参数或返回
    的头像 发表于 11-21 10:27 323次阅读