< prev index next >

src/java.base/share/classes/java/util/StringJoiner.java

Print this page

        

@@ -64,25 +64,38 @@
  * @see java.util.stream.Collectors#joining(CharSequence)
  * @see java.util.stream.Collectors#joining(CharSequence, CharSequence, CharSequence)
  * @since  1.8
 */
 public final class StringJoiner {
+
+    /**
+     * Initial {@link #elts} capacity.
+     */
+    public static final int INITIAL_CAPACITY = 8;
+
     private final String prefix;
     private final String delimiter;
     private final String suffix;
 
-    /** Contains all the string components added so far. */
+    /**
+     * Contains all the string components added so far.
+     */
     private String[] elts;
 
     /** The number of string components added so far. */
     private int size;
 
     /** Total length in chars so far, excluding prefix and suffix. */
     private int len;
 
+    private final int psLen;
+    private final int delimLen;
+
+    private byte coder;
+
     /**
-     * When overriden by the user to be non-null via {@link setEmptyValue}, the
+     * When overriden by the user to be non-null via {@link #setEmptyValue}, the
      * string returned by toString() when no elements have yet been added.
      * When null, prefix + suffix is used as the empty value.
      */
     private String emptyValue;
 

@@ -128,10 +141,14 @@
         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.elts = new String[INITIAL_CAPACITY];
+        this.coder = jla.stringInitialCoder();
+        this.psLen = this.prefix.length() + this.suffix.length();
+        this.delimLen = delimiter.length();
     }
 
     /**
      * Sets the sequence of characters to be used when determining the string
      * representation of this {@code StringJoiner} and no elements have been

@@ -150,48 +167,65 @@
         this.emptyValue = Objects.requireNonNull(emptyValue,
             "The empty value must not be null").toString();
         return this;
     }
 
-    private static int getChars(String s, char[] chars, int start) {
-        int len = s.length();
-        s.getChars(0, len, chars, start);
-        return len;
-    }
-
     /**
      * 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() {
-        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);
+        if (size == 0) {
+            return (emptyValue != null) ?
+                    emptyValue :
+                    prefix + suffix;
+        } else {
+            return doConcat(psLen != 0);
+        }
+    }
+
+    /**
+     * Do the actual concatenation
+     * @param full should include prefix and suffix?
+     * @return concatenated result
+     */
+    private String doConcat(boolean full) {
+        int len = this.len + (size - 1) * delimLen;
+        byte coder = this.coder;
+
+        if (full) {
+            len += psLen;
+            coder = jla.stringMixCoder(coder, prefix);
+            coder = jla.stringMixCoder(coder, suffix);
+        }
+
+        coder = jla.stringMixCoder(coder, delimiter);
+
+        byte[] dst = jla.stringStorageFor(len, coder);
+
+        int idx = 0;
+        if (full) {
+            jla.stringGetChars(prefix, dst, idx, coder);
+            idx += prefix.length();
+        }
+        jla.stringGetChars(elts[0], dst, idx, coder);
+        idx += elts[0].length();
             for (int i = 1; i < size; i++) {
-                k += getChars(delimiter, chars, k);
-                k += getChars(elts[i], chars, k);
+            jla.stringGetChars(delimiter, dst, idx, coder);
+            idx += delimLen;
+            jla.stringGetChars(elts[i], dst, idx, coder);
+            idx += elts[i].length();
             }
+        if (full) {
+            jla.stringGetChars(suffix, dst, idx, coder);
         }
-        k += getChars(suffix, chars, k);
-        return jla.newStringUnsafe(chars);
+        return jla.newStringUnsafe(dst, coder);
     }
 
     /**
      * Adds a copy of the given {@code CharSequence} value as the next
      * element of the {@code StringJoiner} value. If {@code newElement} is

@@ -200,16 +234,13 @@
      * @param  newElement The element to add
      * @return a reference to this {@code StringJoiner}
      */
     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();
+        coder = jla.stringMixCoder(coder, elt);
+        if (size == elts.length) {
+            elts = Arrays.copyOf(elts, size * 2);
         }
         len += elt.length();
         elts[size++] = elt;
         return this;
     }

@@ -233,29 +264,14 @@
      * @throws NullPointerException if the other {@code StringJoiner} is null
      * @return This {@code StringJoiner}
      */
     public StringJoiner merge(StringJoiner other) {
         Objects.requireNonNull(other);
-        if (other.elts == null) {
+        if (other.size == 0) {
             return this;
         }
-        other.compactElts();
-        return add(other.elts[0]);
-    }
-
-    private void compactElts() {
-        if (size > 1) {
-            final char[] chars = new char[len];
-            int i = 1, k = getChars(elts[0], chars, 0);
-            do {
-                k += getChars(delimiter, chars, k);
-                k += getChars(elts[i], chars, k);
-                elts[i] = null;
-            } while (++i < size);
-            size = 1;
-            elts[0] = jla.newStringUnsafe(chars);
-        }
+        return add(other.doConcat(false));
     }
 
     /**
      * Returns the length of the {@code String} representation
      * of this {@code StringJoiner}. Note that if

@@ -265,9 +281,10 @@
      * {@code toString().length()}.
      *
      * @return the length of the current value of {@code StringJoiner}
      */
     public int length() {
-        return (size == 0 && emptyValue != null) ? emptyValue.length() :
-            len + prefix.length() + suffix.length();
+        return (size == 0 && emptyValue != null) ?
+                    emptyValue.length() :
+                    psLen + len + Math.max(0, size - 1) * delimLen;
     }
 }
< prev index next >