src/share/classes/java/util/StringJoiner.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 22,31 ****
--- 22,34 ----
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util;
+ import sun.misc.JavaLangAccess;
+ import sun.misc.SharedSecrets;
+
/**
* {@code StringJoiner} is used to construct a sequence of characters separated
* by a delimiter and optionally starting with a supplied prefix
* and ending with a supplied suffix.
* <p>
*** 65,90 ****
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.
*/
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
--- 68,95 ----
public final class StringJoiner {
private final String prefix;
private final String delimiter;
private final String suffix;
! /** 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;
!
! /**
! * 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;
+ private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
+
/**
* 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
*** 123,133 ****
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
--- 128,137 ----
*** 146,190 ****
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 {
- if (suffix.equals("")) {
- return value.toString();
- } else {
- int initialLength = value.length();
- String result = value.append(suffix).toString();
- // reset value to pre-append initialLength
- 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().append(newElement);
return this;
}
/**
* Adds the contents of the given {@code StringJoiner} without prefix and
--- 150,218 ----
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);
+ for (int i = 1; i < size; i++) {
+ k += getChars(delimiter, chars, k);
+ k += getChars(elts[i], chars, k);
}
}
+ k += getChars(suffix, chars, k);
+ return jla.newStringUnsafe(chars);
+ }
/**
* 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) {
! 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();
! }
! len += elt.length();
! elts[size++] = elt;
return this;
}
/**
* Adds the contents of the given {@code StringJoiner} without prefix and
*** 205,232 ****
* @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
--- 233,261 ----
* @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) {
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);
}
}
/**
* Returns the length of the {@code String} representation
* of this {@code StringJoiner}. Note that if
*** 236,247 ****
* {@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());
}
}
--- 265,273 ----
* {@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();
}
}