StringJoiner源码学习


由Jdk1.8提供,在android 7.0及其以上提供支持。内部基于StringBuilder实现,提供更加便捷的方式来添加分隔符、前缀以及后缀。

用法

由前缀prefix、需要拼接的字符串、分隔符delimiter、以及后缀suffix组成。

//Kotlin
class Demo {
    @RequiresApi(Build.VERSION_CODES.N)
    fun test() {
        var stringJoiner = StringJoiner("", "--->", "<---")
        //result:---><---
        println(stringJoiner.toString())

        stringJoiner = StringJoiner("|", "--->", "<---")
        //result:---><---
        println(stringJoiner.toString())

        stringJoiner.add("1")
        //result:--->1<---
        println(stringJoiner.toString())

        stringJoiner.add("2")
        //result:--->1|2<---
        println(stringJoiner.toString())

        var stringJoiner1 = StringJoiner(",", "前", "后")
        stringJoiner1.add("a")
        stringJoiner1.add("b")
        //result:前a,b后
        println(stringJoiner1.toString())

        stringJoiner.merge(stringJoiner1)
        //结果--->1|2|a,b<---
        println(stringJoiner.toString())

        stringJoiner = StringJoiner("|", "--->", "<---")
        stringJoiner.merge(stringJoiner1)
        //result:--->a,b<---
        println(stringJoiner.toString())
    }
}

源码分析

    public final class StringJoiner {
    //前缀
    private final String prefix;
    //分割符,只存在两个拼接的字符串之间
    private final String delimiter;
    //后缀
    private final String suffix;

    /*
    * StringBuilder value -- at any time, the characters constructed from the
    * prefix, the added element separated by the delimiter, but without the
    * suffix, so that we can more easily add elements without having to jigger
    * the suffix each time.
    */
    //在任何时候只包含前缀+带有分隔符的字符串组成的集合
    private StringBuilder value;

    /*
    * By default, the string consisting of prefix+suffix, returned by
    * toString(), or properties of value, when no elements have yet been added,
    * i.e. when it is empty.  This may be overridden by the user to be some
    * other value including the empty String.
    */
    //由前缀+后缀组成的字符串,默认结果为"";如果没有设置前缀或后缀,将未设置的替换“”,不允许设置null
    //当value==null,toString方法返回的是它
    private String emptyValue;

    /**
    * Constructs a {@code StringJoiner} with no characters in it, with no
    * {@code prefix} or {@code suffix}, and a copy of the supplied
    * {@code delimiter}.
    * If no characters are added to the {@code StringJoiner} and methods
    * accessing the value of it are invoked, it will not return a
    * {@code prefix} or {@code suffix} (or properties thereof) in the result,
    * unless {@code setEmptyValue} has first been called.
    *
    * @param  delimiter the sequence of characters to be used between each
    *         element added to the {@code StringJoiner} value
    * @throws NullPointerException if {@code delimiter} is {@code null}
    */
    public StringJoiner(CharSequence delimiter) {
        this(delimiter, "", "");
    }

    /**
    * Constructs a {@code StringJoiner} with no characters in it using copies
    * of the supplied {@code prefix}, {@code delimiter} and {@code suffix}.
    * If no characters are added to the {@code StringJoiner} and methods
    * accessing the string value of it are invoked, it will return the
    * {@code prefix + suffix} (or properties thereof) in the result, unless
    * {@code setEmptyValue} has first been called.
    *
    * @param  delimiter the sequence of characters to be used between each
    *         element added to the {@code StringJoiner}
    * @param  prefix the sequence of characters to be used at the beginning
    * @param  suffix the sequence of characters to be used at the end
    * @throws NullPointerException if {@code prefix}, {@code delimiter}, or
    *         {@code suffix} is {@code null}
    */
    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;
    }

    /**
    * Sets the sequence of characters to be used when determining the string
    * representation of this {@code StringJoiner} and no elements have been
    * added yet, that is, when it is empty.  A copy of the {@code emptyValue}
    * parameter is made for this purpose. Note that once an add method has been
    * called, the {@code StringJoiner} is no longer considered empty, even if
    * the element(s) added correspond to the empty {@code String}.
    *
    * @param  emptyValue the characters to return as the value of an empty
    *         {@code StringJoiner}
    * @return this {@code StringJoiner} itself so the calls may be chained
    * @throws NullPointerException when the {@code emptyValue} parameter is
    *         {@code null}
    */
    public StringJoiner setEmptyValue(CharSequence emptyValue) {
        this.emptyValue = Objects.requireNonNull(emptyValue,
            "The empty value must not be null").toString();
        return this;
    }

    /**
    * Returns the current value, consisting of the {@code prefix}, the values
    * added so far separated by the {@code delimiter}, and the {@code suffix},
    * unless no elements have been added in which case, the
    * {@code prefix + suffix} or the {@code emptyValue} characters are returned
    *
    * @return the string representation of this {@code StringJoiner}
    */
    @Override
    public String toString() {
       //检测是否已添加需要拼接的字符串
        if (value == null) {
            return emptyValue;
        } else {
            //检查后缀是否为“”,value中包含前缀
            if (suffix.equals("")) {
                return value.toString();
            } else {
                int initialLength = value.length();
                //将value添加后缀并作为结果返回
                String result = value.append(suffix).toString();
                // reset value to pre-append initialLength
                //重新设置value的长度,将value还原到还未添加后缀的状态,也就是说去掉刚添加的后缀,保证下一次add正确性
                value.setLength(initialLength);
                return result;
            }
        }
    }

    /**
    * Adds a copy of the given {@code CharSequence} value as the next
    * element of the {@code StringJoiner} value. If {@code newElement} is
    * {@code null}, then {@code "null"} is added.
    *
    * @param  newElement The element to add
    * @return a reference to this {@code StringJoiner}
    */
    public StringJoiner add(CharSequence newElement) {
        //首先调用prepareBuilder()对value进行操作,如果value为null,则创建value并添加前缀,否则添加分隔符
        //接着添加字符串,就变成前缀+需要拼接的字符串或者前缀+需要拼接的字符串+分割符+需要拼接的字符串
        prepareBuilder().append(newElement);
        return this;
    }

    /**
    * Adds the contents of the given {@code StringJoiner} without prefix and
    * suffix as the next element if it is non-empty. If the given {@code
    * StringJoiner} is empty, the call has no effect.
    *
    * <p>A {@code StringJoiner} is empty if {@link #add(CharSequence) add()}
    * has never been called, and if {@code merge()} has never been called
    * with a non-empty {@code StringJoiner} argument.
    *
    * <p>If the other {@code StringJoiner} is using a different delimiter,
    * then elements from the other {@code StringJoiner} are concatenated with
    * that delimiter and the result is appended to this {@code StringJoiner}
    * as a single element.
    *
    * @param other The {@code StringJoiner} whose contents should be merged
    *              into this one
    * @throws NullPointerException if the other {@code StringJoiner} is null
    * @return This {@code 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;
    }

    private StringBuilder prepareBuilder() {
        if (value != null) {
            value.append(delimiter);
        } else {
            value = new StringBuilder().append(prefix);
        }
        return value;
    }

    /**
    * Returns the length of the {@code String} representation
    * of this {@code StringJoiner}. Note that if
    * no add methods have been called, then the length of the {@code String}
    * representation (either {@code prefix + suffix} or {@code emptyValue})
    * will be returned. The value should be equivalent to
    * {@code toString().length()}.
    *
    * @return the length of the current value of {@code StringJoiner}
    */
    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());
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容