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(); } }