详细分析Java8中的StringJoiner | 对比StringBuilder(附Demo)

news2025/1/18 10:44:35

目录

  • 前言
  • 1. 差异对比
  • 2. 基本知识
  • 3. 源码分析
  • 4. Demo

前言

对于Java的基本知识推荐阅读:

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. 【Java项目】实战CRUD的功能整理(持续更新)

1. 差异对比

展示如何使用StringBuilder和StringJoiner来构建一个以逗号分隔的字符串,并比较这两者的实现方式

import java.util.StringJoiner;

public class StringJoinerExample {
    public static void main(String[] args) {
        // 创建 StringJoiner,使用逗号作为分隔符
        StringJoiner joiner = new StringJoiner(", ");

        // 添加元素
        joiner.add("苹果");
        joiner.add("香蕉");
        joiner.add("橙子");

        // 打印最终结果
        System.out.println("StringJoiner 结果: " + joiner.toString());
    }
}
public class StringBuilderExample {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();

        // 要添加的元素
        String[] fruits = {"苹果", "香蕉", "橙子"};

        for (int i = 0; i < fruits.length; i++) {
            builder.append(fruits[i]); // 添加元素
            if (i < fruits.length - 1) {
                builder.append(", "); // 添加分隔符
            }
        }

        // 打印最终结果
        System.out.println("StringBuilder 结果: " + builder.toString());
    }
}

两者都是输出:苹果, 香蕉, 橙子

两者的差异比较如下:

特性StringJoinerStringBuilder
用法简洁性提供直接的add方法,自动处理分隔符需要手动管理分隔符的添加
空值处理支持自定义空值表示不支持直接的空值表示
合并操作轻松合并其他StringJoiner不支持合并其他实例
代码可读性更清晰,意图明显代码较为复杂,需要更多逻辑控制
适用场景适合需要生成带分隔符的字符串时适合一般的字符串拼接需求

2. 基本知识

StringJoiner 是一个用于构建由分隔符连接的字符串序列的工具,在Java 8中引入,旨在简化字符串连接的过程

主要构造方法

  • StringJoiner(CharSequence delimiter):使用给定的分隔符创建一个StringJoiner实例
  • StringJoiner(CharSequence delimiter, CharSequence prefix, CharSequence suffix):使用给定的分隔符、前缀和后缀创建一个StringJoiner实例

主要方法

  • add(CharSequence newElement):向StringJoiner中添加一个新元素
  • merge(StringJoiner other):将另一个StringJoiner中的元素合并到当前实例
  • toString():返回构建的字符串

3. 源码分析

整体逻辑总结

  1. 构造与初始化:StringJoiner 提供两个构造方法,可以仅指定分隔符,也可以同时指定前缀和后缀。在构造时,会确保前缀、分隔符和后缀不为null

  2. 添加元素:使用 add 方法可以向StringJoiner中添加新元素,内部调用prepareBuilder方法准备一个StringBuilder来构建字符串

  3. 字符串合并:merge 方法允许将另一个StringJoiner的元素合并到当前实例中

  4. 返回拼接结果:toString 方法会返回当前的拼接结果。如果没有添加任何元素,则返回空值表示

  5. 长度管理:length 方法返回当前拼接的字符串长度,考虑了后缀的长度

完整的源码分析如下:

public class StringJoiner {
    private final String delimiter; // 分隔符
    private final String prefix; // 前缀
    private final String suffix; // 后缀
    private StringBuilder value; // 存储拼接结果
    private String emptyValue; // 空值表示

    // 构造方法,接受分隔符
    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");
        // 创建参数的防御性拷贝
        this.prefix = prefix.toString();
        this.delimiter = delimiter.toString();
        this.suffix = suffix.toString();
        this.emptyValue = this.prefix + this.suffix; // 设置空值表示
    }

    // 设置空值表示
    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(); // 拼接后缀
                // 重置值为初始长度,准备下一次添加
                value.setLength(initialLength);
                return result;
            }
        }
    }

    // 添加新元素
    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement); // 准备构建器并添加新元素
        return this;
    }

    // 合并另一个StringJoiner
    public StringJoiner merge(StringJoiner other) {
        Objects.requireNonNull(other); // 确保其他StringJoiner不为null
        if (other.value != null) {
            final int length = other.value.length();
            // 锁定长度以避免干扰
            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() {
        // 返回值的长度加上后缀的长度,或者空值的长度
        return (value != null ? value.length() + suffix.length() : emptyValue.length());
    }
}

具体设计逻辑分析如下:

  1. StringBuilder的使用:StringJoiner内部使用StringBuilder来动态拼接字符串。在调用add方法时,它会立即添加前缀和分隔符,使得每次添加元素时,构建的字符串是连贯的。这样设计的好处在于,后续的合并操作非常简便,因为拼接的逻辑已经被简化,用户只需关注添加元素

  2. 后缀的延迟添加:后缀只有在调用toString或length方法时才会被加上。这种设计方式确保了拼接字符串的格式保持灵活,用户可以根据需要动态生成最终字符串。例如,当有条件地添加后缀时,这种延迟策略避免了不必要的计算和内存占用

  3. 合并操作的便利性:由于StringBuilder在拼接过程中已经考虑了分隔符和前缀,merge操作可以非常简单。它只需将另一个StringJoiner的内容直接附加到当前实例,无需再关心前缀和分隔符的处理,从而减少了复杂性

emptyValue的构造

  1. 构造时生成:在构造StringJoiner时,会生成emptyValue,即由前缀和后缀组成的字符串。这一设计是为了确保在没有元素添加时,能快速返回一个默认值,避免后续计算时出现空值

  2. 用户自定义默认值:用户如果希望有自己的默认值,可以使用setEmptyValue方法。虽然这样需要先构造实例再进行注入,但这种灵活性是设计的一部分,允许用户根据具体需求定制默认值

4. Demo

import java.util.StringJoiner;

public class StringJoinerDemo {
    public static void main(String[] args) {
        // 创建StringJoiner,使用逗号作为分隔符
        StringJoiner joiner = new StringJoiner(", ", "[", "]");

        // 设置自定义的空值表示
        joiner.setEmptyValue("没有元素");

        // 打印当前的结果(没有添加元素)
        System.out.println("当前结果: " + joiner.toString());

        // 添加元素
        joiner.add("苹果");
        joiner.add("香蕉");

        // 打印添加元素后的结果
        System.out.println("添加元素后的结果: " + joiner.toString());

        // 合并另一个StringJoiner
        StringJoiner anotherJoiner = new StringJoiner(", ", "{", "}");
        anotherJoiner.add("西瓜");
        anotherJoiner.add("葡萄");

        // 合并
        joiner.merge(anotherJoiner);

        // 打印合并后的结果
        System.out.println("合并后的结果: " + joiner.toString());
    }
}

截图如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2198579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多种方式确定Linux是CentOS还是Ubuntu

目录 前言正文 前言 对应的基本知识比较少&#xff0c;以下只是记录总结 由于目前使用的是centos&#xff0c;后续找到linux会对应补充 正文 要确定Linux系统是CentOS还是Ubuntu&#xff0c;可以通过以下几种方式进行分析 一、查看发行版信息文件&#xff1a; CentOS&…

UE5运行时动态加载场景角色动画任意搭配-角色及动画(一)

通过《MMD模型及动作一键完美导入UE5》系列文章,我们可以把外部场景、角色、动画资产导入UE5,接下来我们将实现运行时动态加载这些资产,并任意组合搭配。 1、骨骼动画复用 1、大部分模型骨骼是不通用的,比如这些裙子也是有骨骼的,属于模型特有的,但是对于动画来说,很多…

【实时计算 Flink】SQL作业大状态导致反压的调优原理与方法

状态管理不仅影响应用的性能&#xff0c;还关系到系统的稳定性和资源的有效利用。如果状态管理不当&#xff0c;可能会导致性能下降、资源耗尽&#xff0c;甚至系统崩溃。本文为您介绍SQL作业大状态导致反压的调优原理与方法。 运行原理&#xff1a;状态算子的产生 作为一种特…

面试题:Redis(二)

1. 面试题 2. MoreKey案列 事故案例 2.1 生成上如何限制key*/flushdb/flushall等危险命令的使用&#xff1f; 通过redis.conf配置文件中在SECURITY选项中禁用这些命令 2.2 不用key*避免卡顿那用什么&#xff1f; 用scan命令&#xff0c;类似mysql中的limit命令 语法&…

数学建模算法与应用 第2章 整数规划及其求解方法

目录 2.1 概述 2.2 0-1整数规划模型 2.3 分枝定界法&#xff08;Branch and Bound&#xff09; 2.4 蒙特卡洛法&#xff08;随机取样法&#xff09; Matlab代码示例&#xff1a;蒙特卡洛法求解简单整数规划 2.5 整数规划的计算机求解工具 习题 2 总结 整数规划是线性规…

Window11 安装Java21教程

随着Java版本的迭代&#xff0c;最新的长期支持版本已经更新到Java21了&#xff0c;虽然笔者许多代码还是当年用Java8写的&#xff0c;但抱残守缺从来不适合IT人员&#xff0c;该来的我们始终要欣然面对。 其实随着各项技术的发展&#xff0c;Java许多组件现在其实都不需要或者…

Authentication Lab | Client Side Auth

关注这个靶场的其它相关笔记&#xff1a;Authentication Lab —— 靶场笔记合集-CSDN博客 0x01&#xff1a;Client Side Auth 前情提要 有些时候&#xff0c;开发人员会将身份验证的逻辑写于前端&#xff0c;这样写是十分不安全的&#xff0c;因为前端的代码几乎全部都是可见的…

借助微软 Teams 中的 Tableau,加速数据驱动型决策与协作流程

Tableau 应用已正式上线 Teams 商店&#xff01;如此&#xff0c;企业就能以一种更可靠和安全的方式在 Teams 中共享数据见解。 让团队能快速协作做出数据驱动型决策不再是可选项&#xff0c;而是业务成功的必备条件。 在几个月前的TC24 大会上&#xff0c;Tableau 再次表明了…

关于Amazon Linux 2023的版本及包管理器

在亚马逊上创建EC2实例时&#xff0c;会看到有一个Amazon Linux镜像。 那这个镜像与其他Linux有什么关系和区别呢&#xff1f; 网站是介绍&#xff1a;Amazon Linux 2023 是基于 Linux 的现代化通用操作系统&#xff0c;提供 5 年的长期支持。它针对 AWS 进行了优化&#xff0…

Stable Diffusion最新版nowebui的api使用详解

最近在使用stable diffusion最新版的Stable Diffusion WebUI Forge进行api调用,下面来一步一步的进行展开吧!!! 1、下载lllyasviel/stable-diffusion-webui-forge GitHub - lllyasviel/stable-diffusion-webui-forgeContribute to lllyasviel/stable-diffusion-webui-for…

gaussdb hccdp认证思考题01 GaussDB数据库介绍

01_GaussDB数据库介绍 1. &#xff08;判断题&#xff09;gsql是一款运行在Windows操作系统上的图形界面SQL客户端工具&#xff0c;用于连接GaussDB集群中的数据库以及管理数据库对象。 --错。

120页满分PPT | 企业级业务架构和IT架构规划方案

方案内容综述 方案涵盖了从战略分析到具体实施路径的内容。提出了IT架构规划的工作思路&#xff0c;包括项目启动、部门访谈、资料收集、内部数据库搜索与先进实践研究等步骤&#xff0c;旨在通过这些步骤完成现状及差距分析&#xff0c;并基于此设计未来的应用架构、数据架构…

应用UX体验标准

1、应用导航 标准编号 2.1.1.1 系统返回 标准描述 所有界面都可以执行系统返回操作。 除一级界面外&#xff0c;所有全屏界面均需要提供返回/关闭/取消按钮。(全屏沉浸式场景除外) 测试方法 使用侧边返回手势&#xff0c;验证当前应用界面是否可以执行系统返回操作。检查…

ML 系列:机器学习和深度学习的深层次总结(14) — 逻辑回归(第 3 部分 — 实施)

目录 一、说明 二、数据集说明 三、探索性数据分析 3.1. 查找 null 值 3.2. 数据预处理 3.3. 独特价值 3.4. 两种类型&#xff08;恶性、良性&#xff09;之间的数据传播 3.5. 特征选择和降维 3.5.1.特征选择 3.5.2 降维 &#xff08;PCA&#xff09; 3.6. 选择数据的两个重要特…

【测试】——Loadrunner 介绍与使用

&#x1f4d6; 前言&#xff1a;LoadRunner是一款开源桌面应用软件&#xff0c;可用来模拟用户负载完成性能测试工作&#xff0c;LoadRunner的功能在版本不断升级的过程中已经十分强大&#xff0c;现在很多互联网公司都在使用LoadRunner来完成产品或者Loadrunner是业界公认的权…

纠删码参数自适应匹配问题ECP-AMP实验方案(二)

6.方法设计 6.1.数据获取 为了收集不同的文件大小和纠删码参数对性能指标的影响&#xff0c;本文在Hadoop平台上进行了模拟实验。Hadoop是一种开源的分布式存储和计算框架&#xff0c;它可以支持不同类型的纠删码&#xff0c;并提供了一些应用程序接口和工具来测试和评估纠删…

最大异或对(每周一类)

今天我们来看这个最大异或类这道题 最大异或对 1.首先&#xff0c;我们先来了解一下异或是什么&#xff0c;之后还要讲一下同或。 众所周知&#xff0c;数字在计算机中是由二进制来表示的&#xff0c;比如十进制的7&#xff0c;用二进制表示就是 111&#xff0c;十进制的3&…

SpringBoot+Activiti7工作流使用进阶实例-高亮显示BPMN流程图( SpringBoot+Activiti+mybatis+shiro实现)

文章目录 说明绘制流程图排他网关设置任务节点设置创建工程修改 pom.xml 文件准备数据库的表和测试数据修改 application.yml 文件配置静态资源Shiro 相关配置ShiroConfiguration.javaMyShiroRealm.java流程控制器添加静态的资源和模板页面运行结果截图源码地址说明 使用 Spri…

量子数字签名概述

我们都知道&#xff0c;基于量子力学原理研究密钥生成和使用的学科称为量子密码学。其内容包括了量子密钥分发、量子秘密共享、量子指纹识别、量子比特承诺、量子货币、秘密通信扩展量子密钥、量子安全计算、量子数字签名、量子隐性传态等。虽然各种技术发展的状态不同&#xf…

45岁被裁员的程序员,何去何从?

在当今快速变化的技术行业&#xff0c;职业生涯的稳定性受到挑战。在45岁被裁员&#xff0c;对很多程序员来说&#xff0c;可能是一种惊慌失措的体验。然而&#xff0c;这个阶段也可以被视为一个重新审视和调整方向的机会。本文将对可能的出路进行全方位的分析&#xff0c;并提…