# HG changeset patch # User redestad # Date 1582725726 -3600 # Wed Feb 26 15:02:06 2020 +0100 # Node ID dab445ecf0cb31861bd097526c9e3f4ad6b3409a # Parent d6b968af8b65cd4cd745811057d59daddf54c34a 8240094: Optimize empty substring handling Reviewed-by: redestad, igerasim, jlaskey Contributed-by: sergei.tsypanov@yandex.ru diff --git a/src/java.base/share/classes/java/lang/String.java b/src/java.base/share/classes/java/lang/String.java --- a/src/java.base/share/classes/java/lang/String.java +++ b/src/java.base/share/classes/java/lang/String.java @@ -1819,7 +1819,7 @@ * @param src the characters being searched. * @param srcCoder coder handles the mapping between bytes/chars * @param srcCount count of the source string. - * @param tgt the characters being searched for. + * @param tgtStr the characters being searched for. * @param fromIndex the index to begin searching from. */ static int lastIndexOf(byte[] src, byte srcCoder, int srcCount, @@ -1900,10 +1900,10 @@ public String substring(int beginIndex, int endIndex) { int length = length(); checkBoundsBeginEnd(beginIndex, endIndex, length); - int subLen = endIndex - beginIndex; if (beginIndex == 0 && endIndex == length) { return this; } + int subLen = endIndex - beginIndex; return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) : StringUTF16.newString(value, beginIndex, subLen); } diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -630,17 +630,11 @@ public static String stripLeading(byte[] value) { int left = indexOfNonWhitespace(value); - if (left == value.length) { - return ""; - } return (left != 0) ? newString(value, left, value.length - left) : null; } public static String stripTrailing(byte[] value) { int right = lastIndexOfNonWhitespace(value); - if (right == 0) { - return ""; - } return (right != value.length) ? newString(value, 0, right) : null; } @@ -764,6 +758,9 @@ } public static String newString(byte[] val, int index, int len) { + if (len == 0) { + return ""; + } return new String(Arrays.copyOfRange(val, index, index + len), LATIN1); } diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -1016,18 +1016,12 @@ public static String stripLeading(byte[] value) { int length = value.length >>> 1; int left = indexOfNonWhitespace(value); - if (left == length) { - return ""; - } return (left != 0) ? newString(value, left, length - left) : null; } public static String stripTrailing(byte[] value) { int length = value.length >>> 1; int right = lastIndexOfNonWhitespace(value); - if (right == 0) { - return ""; - } return (right != length) ? newString(value, 0, right) : null; } @@ -1132,6 +1126,9 @@ } public static String newString(byte[] val, int index, int len) { + if (len == 0) { + return ""; + } if (String.COMPACT_STRINGS) { byte[] buf = compress(val, index, len); if (buf != null) { diff --git a/test/micro/org/openjdk/bench/java/lang/StringSubstring.java b/test/micro/org/openjdk/bench/java/lang/StringSubstring.java --- a/test/micro/org/openjdk/bench/java/lang/StringSubstring.java +++ b/test/micro/org/openjdk/bench/java/lang/StringSubstring.java @@ -28,6 +28,9 @@ @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) +@Fork(value = 3) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @State(Scope.Benchmark) public class StringSubstring { @@ -42,4 +45,12 @@ public String from26toEnd1() { return s.substring(26, s.length()); } + + /** + * An empty substring should not allocate a new String + */ + @Benchmark + public String empty() { + return s.substring(17, 17); + } }