From 3a1d431c7df30cffd70174c635879b0d9dd9f745 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 11 Apr 2017 10:11:54 +0200 Subject: [PATCH] Optimize StringUtils.replace/deleteAny for common no-op case Issue: SPR-15430 --- .../org/springframework/util/StringUtils.java | 50 +++++++++++++++++-- .../util/StringUtilsTests.java | 8 +-- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/util/StringUtils.java b/spring-core/src/main/java/org/springframework/util/StringUtils.java index b6eb57cac19..e7132384983 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -140,6 +140,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return false; } + int strLen = str.length(); for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(str.charAt(i))) { @@ -174,6 +175,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return false; } + int strLen = str.length(); for (int i = 0; i < strLen; i++) { if (Character.isWhitespace(str.charAt(i))) { @@ -204,6 +206,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return str; } + StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { sb.deleteCharAt(0); @@ -225,6 +228,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return str; } + int len = str.length(); StringBuilder sb = new StringBuilder(str.length()); for (int i = 0; i < len; i++) { @@ -246,6 +250,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return str; } + StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) { sb.deleteCharAt(0); @@ -263,6 +268,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return str; } + StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) { sb.deleteCharAt(sb.length() - 1); @@ -280,6 +286,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return str; } + StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) { sb.deleteCharAt(0); @@ -297,6 +304,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return str; } + StringBuilder sb = new StringBuilder(str); while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) { sb.deleteCharAt(sb.length() - 1); @@ -322,6 +330,7 @@ public abstract class StringUtils { if (str.length() < prefix.length()) { return false; } + String lcStr = str.substring(0, prefix.length()).toLowerCase(); String lcPrefix = prefix.toLowerCase(); return lcStr.equals(lcPrefix); @@ -376,6 +385,7 @@ public abstract class StringUtils { if (!hasLength(str) || !hasLength(sub)) { return 0; } + int count = 0; int pos = 0; int idx; @@ -398,10 +408,14 @@ public abstract class StringUtils { if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { return inString; } - StringBuilder sb = new StringBuilder(); - int pos = 0; // our position in the old string int index = inString.indexOf(oldPattern); - // the index of an occurrence we've found, or -1 + if (index == -1) { + // no occurrence -> can return input as-is + return inString; + } + + StringBuilder sb = new StringBuilder(inString.length()); + int pos = 0; // our position in the old string int patLen = oldPattern.length(); while (index >= 0) { sb.append(inString.substring(pos, index)); @@ -409,8 +423,8 @@ public abstract class StringUtils { pos = index + patLen; index = inString.indexOf(oldPattern, pos); } + // append any characters to the right of a match sb.append(inString.substring(pos)); - // remember to append any characters to the right of a match return sb.toString(); } @@ -435,7 +449,8 @@ public abstract class StringUtils { if (!hasLength(inString) || !hasLength(charsToDelete)) { return inString; } - StringBuilder sb = new StringBuilder(); + + StringBuilder sb = new StringBuilder(inString.length()); for (int i = 0; i < inString.length(); i++) { char c = inString.charAt(i); if (charsToDelete.indexOf(c) == -1) { @@ -518,6 +533,7 @@ public abstract class StringUtils { if (!hasLength(str)) { return str; } + char baseChar = str.charAt(0); char updatedChar; if (capitalize) { @@ -529,6 +545,7 @@ public abstract class StringUtils { if (baseChar == updatedChar) { return str; } + char[] chars = str.toCharArray(); chars[0] = updatedChar; return new String(chars, 0, chars.length); @@ -544,6 +561,7 @@ public abstract class StringUtils { if (path == null) { return null; } + int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path); } @@ -558,14 +576,17 @@ public abstract class StringUtils { if (path == null) { return null; } + int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); if (extIndex == -1) { return null; } + int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); if (folderIndex > extIndex) { return null; } + return path.substring(extIndex + 1); } @@ -580,14 +601,17 @@ public abstract class StringUtils { if (path == null) { return null; } + int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR); if (extIndex == -1) { return path; } + int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR); if (folderIndex > extIndex) { return path; } + return path.substring(0, extIndex); } @@ -756,8 +780,10 @@ public abstract class StringUtils { String[] parts = tokenizeToStringArray(localeString, "_ ", false, false); String language = (parts.length > 0 ? parts[0] : ""); String country = (parts.length > 1 ? parts[1] : ""); + validateLocalePart(language); validateLocalePart(country); + String variant = ""; if (parts.length > 2) { // There is definitely a variant, and it is everything after the country @@ -825,6 +851,7 @@ public abstract class StringUtils { if (ObjectUtils.isEmpty(array)) { return new String[] {str}; } + String[] newArr = new String[array.length + 1]; System.arraycopy(array, 0, newArr, 0, array.length); newArr[array.length] = str; @@ -846,6 +873,7 @@ public abstract class StringUtils { if (ObjectUtils.isEmpty(array2)) { return array1; } + String[] newArr = new String[array1.length + array2.length]; System.arraycopy(array1, 0, newArr, 0, array1.length); System.arraycopy(array2, 0, newArr, array1.length, array2.length); @@ -869,6 +897,7 @@ public abstract class StringUtils { if (ObjectUtils.isEmpty(array2)) { return array1; } + List result = new ArrayList<>(); result.addAll(Arrays.asList(array1)); for (String str : array2) { @@ -888,6 +917,7 @@ public abstract class StringUtils { if (ObjectUtils.isEmpty(array)) { return new String[0]; } + Arrays.sort(array); return array; } @@ -903,6 +933,7 @@ public abstract class StringUtils { if (collection == null) { return null; } + return collection.toArray(new String[collection.size()]); } @@ -917,6 +948,7 @@ public abstract class StringUtils { if (enumeration == null) { return null; } + List list = Collections.list(enumeration); return list.toArray(new String[list.size()]); } @@ -931,6 +963,7 @@ public abstract class StringUtils { if (ObjectUtils.isEmpty(array)) { return new String[0]; } + String[] result = new String[array.length]; for (int i = 0; i < array.length; i++) { String element = array[i]; @@ -949,6 +982,7 @@ public abstract class StringUtils { if (ObjectUtils.isEmpty(array)) { return array; } + Set set = new LinkedHashSet<>(); for (String element : array) { set.add(element); @@ -973,6 +1007,7 @@ public abstract class StringUtils { if (offset < 0) { return null; } + String beforeDelimiter = toSplit.substring(0, offset); String afterDelimiter = toSplit.substring(offset + delimiter.length()); return new String[] {beforeDelimiter, afterDelimiter}; @@ -1013,6 +1048,7 @@ public abstract class StringUtils { if (ObjectUtils.isEmpty(array)) { return null; } + Properties result = new Properties(); for (String element : array) { if (charsToDelete != null) { @@ -1073,6 +1109,7 @@ public abstract class StringUtils { if (str == null) { return null; } + StringTokenizer st = new StringTokenizer(str, delimiters); List tokens = new ArrayList<>(); while (st.hasMoreTokens()) { @@ -1126,6 +1163,7 @@ public abstract class StringUtils { if (delimiter == null) { return new String[] {str}; } + List result = new ArrayList<>(); if ("".equals(delimiter)) { for (int i = 0; i < str.length(); i++) { @@ -1187,6 +1225,7 @@ public abstract class StringUtils { if (CollectionUtils.isEmpty(coll)) { return ""; } + StringBuilder sb = new StringBuilder(); Iterator it = coll.iterator(); while (it.hasNext()) { @@ -1233,6 +1272,7 @@ public abstract class StringUtils { if (arr.length == 1) { return ObjectUtils.nullSafeToString(arr[0]); } + StringBuilder sb = new StringBuilder(); for (int i = 0; i < arr.length; i++) { if (i > 0) { diff --git a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java index a1abde60b25..2a4c61cc1ec 100644 --- a/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/util/StringUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -177,15 +177,15 @@ public class StringUtilsTests { // Non match: no change s = StringUtils.replace(inString, "qwoeiruqopwieurpoqwieur", newPattern); - assertTrue("Replace non matched is equal", s.equals(inString)); + assertSame("Replace non-matched is returned as-is", inString, s); // Null new pattern: should ignore s = StringUtils.replace(inString, oldPattern, null); - assertTrue("Replace non matched is equal", s.equals(inString)); + assertSame("Replace non-matched is returned as-is", inString, s); // Null old pattern: should ignore s = StringUtils.replace(inString, null, newPattern); - assertTrue("Replace non matched is equal", s.equals(inString)); + assertSame("Replace non-matched is returned as-is", inString, s); } @Test