jdk/src/java.base/share/classes/java/util/StringJoiner.java
changeset 25991 e48157b42439
parent 25967 0060a996fe1c
parent 25859 3317bb8137f4
child 32834 e1dca5fe4de3
equal deleted inserted replaced
25876:d06a6d3c66c0 25991:e48157b42439
     1 /*
     1 /*
     2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
     2  * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     7  * published by the Free Software Foundation.  Oracle designates this
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 package java.util;
    25 package java.util;
       
    26 
       
    27 import sun.misc.JavaLangAccess;
       
    28 import sun.misc.SharedSecrets;
    26 
    29 
    27 /**
    30 /**
    28  * {@code StringJoiner} is used to construct a sequence of characters separated
    31  * {@code StringJoiner} is used to construct a sequence of characters separated
    29  * by a delimiter and optionally starting with a supplied prefix
    32  * by a delimiter and optionally starting with a supplied prefix
    30  * and ending with a supplied suffix.
    33  * and ending with a supplied suffix.
    65 public final class StringJoiner {
    68 public final class StringJoiner {
    66     private final String prefix;
    69     private final String prefix;
    67     private final String delimiter;
    70     private final String delimiter;
    68     private final String suffix;
    71     private final String suffix;
    69 
    72 
    70     /*
    73     /** Contains all the string components added so far. */
    71      * StringBuilder value -- at any time, the characters constructed from the
    74     private String[] elts;
    72      * prefix, the added element separated by the delimiter, but without the
    75 
    73      * suffix, so that we can more easily add elements without having to jigger
    76     /** The number of string components added so far. */
    74      * the suffix each time.
    77     private int size;
    75      */
    78 
    76     private StringBuilder value;
    79     /** Total length in chars so far, excluding prefix and suffix. */
    77 
    80     private int len;
    78     /*
    81 
    79      * By default, the string consisting of prefix+suffix, returned by
    82     /**
    80      * toString(), or properties of value, when no elements have yet been added,
    83      * When overriden by the user to be non-null via {@link setEmptyValue}, the
    81      * i.e. when it is empty.  This may be overridden by the user to be some
    84      * string returned by toString() when no elements have yet been added.
    82      * other value including the empty String.
    85      * When null, prefix + suffix is used as the empty value.
    83      */
    86      */
    84     private String emptyValue;
    87     private String emptyValue;
       
    88 
       
    89     private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
    85 
    90 
    86     /**
    91     /**
    87      * Constructs a {@code StringJoiner} with no characters in it, with no
    92      * Constructs a {@code StringJoiner} with no characters in it, with no
    88      * {@code prefix} or {@code suffix}, and a copy of the supplied
    93      * {@code prefix} or {@code suffix}, and a copy of the supplied
    89      * {@code delimiter}.
    94      * {@code delimiter}.
   123         Objects.requireNonNull(suffix, "The suffix must not be null");
   128         Objects.requireNonNull(suffix, "The suffix must not be null");
   124         // make defensive copies of arguments
   129         // make defensive copies of arguments
   125         this.prefix = prefix.toString();
   130         this.prefix = prefix.toString();
   126         this.delimiter = delimiter.toString();
   131         this.delimiter = delimiter.toString();
   127         this.suffix = suffix.toString();
   132         this.suffix = suffix.toString();
   128         this.emptyValue = this.prefix + this.suffix;
       
   129     }
   133     }
   130 
   134 
   131     /**
   135     /**
   132      * Sets the sequence of characters to be used when determining the string
   136      * Sets the sequence of characters to be used when determining the string
   133      * representation of this {@code StringJoiner} and no elements have been
   137      * representation of this {@code StringJoiner} and no elements have been
   146         this.emptyValue = Objects.requireNonNull(emptyValue,
   150         this.emptyValue = Objects.requireNonNull(emptyValue,
   147             "The empty value must not be null").toString();
   151             "The empty value must not be null").toString();
   148         return this;
   152         return this;
   149     }
   153     }
   150 
   154 
       
   155     private static int getChars(String s, char[] chars, int start) {
       
   156         int len = s.length();
       
   157         s.getChars(0, len, chars, start);
       
   158         return len;
       
   159     }
       
   160 
   151     /**
   161     /**
   152      * Returns the current value, consisting of the {@code prefix}, the values
   162      * Returns the current value, consisting of the {@code prefix}, the values
   153      * added so far separated by the {@code delimiter}, and the {@code suffix},
   163      * added so far separated by the {@code delimiter}, and the {@code suffix},
   154      * unless no elements have been added in which case, the
   164      * unless no elements have been added in which case, the
   155      * {@code prefix + suffix} or the {@code emptyValue} characters are returned
   165      * {@code prefix + suffix} or the {@code emptyValue} characters are returned.
   156      *
   166      *
   157      * @return the string representation of this {@code StringJoiner}
   167      * @return the string representation of this {@code StringJoiner}
   158      */
   168      */
   159     @Override
   169     @Override
   160     public String toString() {
   170     public String toString() {
   161         if (value == null) {
   171         final String[] elts = this.elts;
       
   172         if (elts == null && emptyValue != null) {
   162             return emptyValue;
   173             return emptyValue;
   163         } else {
   174         }
   164             if (suffix.equals("")) {
   175         final int size = this.size;
   165                 return value.toString();
   176         final int addLen = prefix.length() + suffix.length();
   166             } else {
   177         if (addLen == 0) {
   167                 int initialLength = value.length();
   178             compactElts();
   168                 String result = value.append(suffix).toString();
   179             return size == 0 ? "" : elts[0];
   169                 // reset value to pre-append initialLength
   180         }
   170                 value.setLength(initialLength);
   181         final String delimiter = this.delimiter;
   171                 return result;
   182         final char[] chars = new char[len + addLen];
       
   183         int k = getChars(prefix, chars, 0);
       
   184         if (size > 0) {
       
   185             k += getChars(elts[0], chars, k);
       
   186             for (int i = 1; i < size; i++) {
       
   187                 k += getChars(delimiter, chars, k);
       
   188                 k += getChars(elts[i], chars, k);
   172             }
   189             }
   173         }
   190         }
       
   191         k += getChars(suffix, chars, k);
       
   192         return jla.newStringUnsafe(chars);
   174     }
   193     }
   175 
   194 
   176     /**
   195     /**
   177      * Adds a copy of the given {@code CharSequence} value as the next
   196      * Adds a copy of the given {@code CharSequence} value as the next
   178      * element of the {@code StringJoiner} value. If {@code newElement} is
   197      * element of the {@code StringJoiner} value. If {@code newElement} is
   180      *
   199      *
   181      * @param  newElement The element to add
   200      * @param  newElement The element to add
   182      * @return a reference to this {@code StringJoiner}
   201      * @return a reference to this {@code StringJoiner}
   183      */
   202      */
   184     public StringJoiner add(CharSequence newElement) {
   203     public StringJoiner add(CharSequence newElement) {
   185         prepareBuilder().append(newElement);
   204         final String elt = String.valueOf(newElement);
       
   205         if (elts == null) {
       
   206             elts = new String[8];
       
   207         } else {
       
   208             if (size == elts.length)
       
   209                 elts = Arrays.copyOf(elts, 2 * size);
       
   210             len += delimiter.length();
       
   211         }
       
   212         len += elt.length();
       
   213         elts[size++] = elt;
   186         return this;
   214         return this;
   187     }
   215     }
   188 
   216 
   189     /**
   217     /**
   190      * Adds the contents of the given {@code StringJoiner} without prefix and
   218      * Adds the contents of the given {@code StringJoiner} without prefix and
   205      * @throws NullPointerException if the other {@code StringJoiner} is null
   233      * @throws NullPointerException if the other {@code StringJoiner} is null
   206      * @return This {@code StringJoiner}
   234      * @return This {@code StringJoiner}
   207      */
   235      */
   208     public StringJoiner merge(StringJoiner other) {
   236     public StringJoiner merge(StringJoiner other) {
   209         Objects.requireNonNull(other);
   237         Objects.requireNonNull(other);
   210         if (other.value != null) {
   238         if (other.elts == null) {
   211             final int length = other.value.length();
   239             return this;
   212             // lock the length so that we can seize the data to be appended
   240         }
   213             // before initiate copying to avoid interference, especially when
   241         other.compactElts();
   214             // merge 'this'
   242         return add(other.elts[0]);
   215             StringBuilder builder = prepareBuilder();
   243     }
   216             builder.append(other.value, other.prefix.length(), length);
   244 
   217         }
   245     private void compactElts() {
   218         return this;
   246         if (size > 1) {
   219     }
   247             final char[] chars = new char[len];
   220 
   248             int i = 1, k = getChars(elts[0], chars, 0);
   221     private StringBuilder prepareBuilder() {
   249             do {
   222         if (value != null) {
   250                 k += getChars(delimiter, chars, k);
   223             value.append(delimiter);
   251                 k += getChars(elts[i], chars, k);
   224         } else {
   252                 elts[i] = null;
   225             value = new StringBuilder().append(prefix);
   253             } while (++i < size);
   226         }
   254             size = 1;
   227         return value;
   255             elts[0] = jla.newStringUnsafe(chars);
       
   256         }
   228     }
   257     }
   229 
   258 
   230     /**
   259     /**
   231      * Returns the length of the {@code String} representation
   260      * Returns the length of the {@code String} representation
   232      * of this {@code StringJoiner}. Note that if
   261      * of this {@code StringJoiner}. Note that if
   236      * {@code toString().length()}.
   265      * {@code toString().length()}.
   237      *
   266      *
   238      * @return the length of the current value of {@code StringJoiner}
   267      * @return the length of the current value of {@code StringJoiner}
   239      */
   268      */
   240     public int length() {
   269     public int length() {
   241         // Remember that we never actually append the suffix unless we return
   270         return (size == 0 && emptyValue != null) ? emptyValue.length() :
   242         // the full (present) value or some sub-string or length of it, so that
   271             len + prefix.length() + suffix.length();
   243         // we can add on more if we need to.
       
   244         return (value != null ? value.length() + suffix.length() :
       
   245                 emptyValue.length());
       
   246     }
   272     }
   247 }
   273 }