StringJoiner

导读:本篇文章讲解 StringJoiner,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

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

String 类也于 Java 8 新增了两个静态重载方法:join(CharSequence delimiter, CharSequence... elements) : Stringjoin(CharSequence delimiter,Iterable<? extends CharSequence> elements) : String,而这两个方法的实现使用的就是 StringJoiner。

API介绍及使用

构造方法

StringJoiner 有两个构造方法,第一个构造要求依次传入分隔符,前缀,后缀。第二个构造则只要求传入分隔符即可,没有前缀和后缀(前缀和后缀默认为空字符串)

    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();
        this.emptyValue = this.prefix + this.suffix;
    }

使用效果如下:

    public static void main(String[] args) {
        StringJoiner stringJoiner1 = new StringJoiner(" - ");
        StringJoiner stringJoiner2 = new StringJoiner(" - ","(",")");
    }

add方法

创建好对象之后,我们主要使用的是 StringJoiner 的 add 方法,通过它可以追加要拼接的元素。

    add(CharSequence newElement) : StringJoiner
    public static void main(String[] args) {
        StringJoiner stringJoiner1 = new StringJoiner(" - ");
        StringJoiner stringJoiner2 = new StringJoiner(" - ","(",")");

        StringJoiner result1 = stringJoiner1.add("hello").add("world"); // hello - world
        StringJoiner result2 = stringJoiner1.add("hello").add("world"); // (hello - world)
    }

merge方法

如果我们需要合并两个 StringJoiner 的内容,可以使用 merge 方法。

    merge(StringJoiner other) : StringJoiner
    public static void main(String[] args) {
        StringJoiner stringJoiner1 = new StringJoiner(" - ");
        StringJoiner stringJoiner2 = new StringJoiner(" - ","(",")");

        stringJoiner1.add("hello").add("world");
        stringJoiner2.add("hello").add("world");

        // hello1 - world1 - hello2 - world2
        StringJoiner merge1 = stringJoiner1.merge(stringJoiner2); 

        // (hello2 - world2 - hello1 - world1 - hello2 - world2)
        StringJoiner merge2 = stringJoiner2.merge(stringJoiner1); 
    }

谁调用则合并时以谁为主

源码解析

首先,在使用 StringJoiner 时,我们会先调用 构造方法 来创建对象,这时候 StringJoiner 会拿着构造参数来初始化它的前缀、分隔符、后缀三个属性,然后将前缀和后缀拼接一下用来初始化 emptyValue 属性,这个属性是用来表示一个元素也未添加过的情况(空值)。

    public final class StringJoiner {
        // 前缀
        private final String prefix;
        // 分隔符
        private final String delimiter;
        // 后缀
        private final String suffix;
    
        // StringJoiner本质在使用StringBuilder实现字符串拼接
        private StringBuilder value;
    
        // 空值
        private String emptyValue;
    
        // 不带前缀和后缀,只指定分隔符来创建StringJoiner
        public StringJoiner(CharSequence delimiter) {
            this(delimiter, "", "");
        }
    
        // 依次指定分隔符、前缀、后缀来创建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;
        }
    }

随后,当我们执行 add 方法 来添加元素时,有两种不同的处理情况。

如果是添加第一个元素,StringJoiner 会先初始化内部的 StringBuilder,然后用 StringBuilder 依次追加好前缀及第一个元素。

如果不是添加第一个元素,StringJoiner 内部的 StringBuilder 则会依次追加好分隔符及元素。

    // 使用StringBuilder追加元素
    public StringJoiner add(CharSequence newElement) {
        prepareBuilder().append(newElement);
        return this;
    }

	// 对内部的StringBuilder进行预处理
    // 第一次调用该方法时,初始化StringBuilder并追加前缀
    // 第二次及后续调用该方法时,追加分隔符
    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

当我们调用 toString 方法 来获取 StringJoiner 内存储的字符串时,也有两种不同的处理情况。

如果是没有添加过元素,StringJoiner 会直接返回给我们 emptyValue(空值) 属性值。

如果添加过元素,StringJoiner 会判断后缀是否为空字符串,如果后缀为空字符串则直接将内部 StringBuilder 转换为字符串即可;但如果后缀不是空字符串,则首先在 StringBuilder 内追加后缀元素并转换为字符串返回,再之后有点意思的是,StringJoiner 为了让我们调用完 toString 方法后依然可以正常添加元素,它会将 StringBuilder 刚添加的后缀再去除。

	// 将StringJoiner内容转换为字符串 
	@Override
    public String toString() {
        // 如果StringBuilder为null,说明没有添加过元素
        // 返回默认空值
        if (value == null) {
            return emptyValue;
        } else {
            // 如果StringBuilder不为null,说明添加过元素
            // 判断后缀是否为空字符串
            if (suffix.equals("")) {
                // 直接返回StringBuilder的字符串内容即可
                return value.toString();
            } else {
                // 如果后缀不是空字符串
                // 获取当前StringBuilder的字符串长度
                int initialLength = value.length();
                // StringBuilder拼接后缀并转换为字符串
                String result = value.append(suffix).toString();
                // 将StringBuilder拼接的后缀再去除,为了可以后续继续添加元素
                value.setLength(initialLength);
                return result;
            }
        }
    }

其他方法源码

    // 合并其他StringJoiner内容
    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;
    }


    // 获取StringJoiner长度
    public int length() {
        // 如果StringBuilder不为空,StringBuilder长度 + 后缀长度就是StringJoiner长度
        // 后缀在调用toString()方法时才会追加,之所以如此设定,是因为提前拼接后缀会导致无法再追加新元素
        return (value != null ? value.length() + suffix.length() :
                emptyValue.length());
    }


    // 设置空值
    public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }

String的join方法

Java 8 中不仅提供了 StringJoiner ,还在 String 类中提供了两个新方法,这两个新方法内部的实现也是通过 StringJoiner

    // 将可变参中的元素们通过指定分隔符拼接为字符串
    String message = String.join("-", "Java", "is", "cool");
    System.out.println(message); // Java-is-cool



    // 将集合中的元素通过指定分隔符拼接为字符串
    List<String> strings1 = new LinkedList<>();
    strings1.add("Java");strings1.add("is");
    strings1.add("cool");
    String message1 = String.join(" ", strings1);
    System.out.println(message1); // Java is cool


    Set<String> strings2 = new LinkedHashSet<>();
    strings2.add("Java"); strings2.add("is");
    strings2.add("very"); strings2.add("cool");
    String message2 = String.join("-", strings2);
    System.out.println(message2); // Java-is-very-cool

源码解析

String 类新增的两个静态方法,内部实现代码也非常简单,它们都利用的是 StringJoiner 提供的传入分隔符的单参构造,然后通过遍历传入的可变参数或集合来向 StringJoiner 内添加元素,最后将 StringJoiner 转换为字符串返回。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
    
    // 对字符串列表通过指定分隔符拼接
    public static String join(CharSequence delimiter, CharSequence... elements) {
        // 参数判空
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // 创建StringJoiner对象,指定分隔符
        StringJoiner joiner = new StringJoiner(delimiter);
        // 遍历 可变参 数组,并将元素添加到StringJoiner
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        // 将StringJoiner转换为字符串
        return joiner.toString();
    }

    // 对集合中的所有元素通过指定分隔符拼接
    public static String join(CharSequence delimiter,
            Iterable<? extends CharSequence> elements) {
        // 参数判空
        Objects.requireNonNull(delimiter);
        Objects.requireNonNull(elements);
        // 创建StringJoiner对象,指定分隔符
        StringJoiner joiner = new StringJoiner(delimiter);
        // 遍历集合,并将元素添加到StringJoiner
        for (CharSequence cs: elements) {
            joiner.add(cs);
        }
        // 将StringJoiner转换为字符串
        return joiner.toString();
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/95913.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!