< 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 >