Skip to content

Commit 865953f

Browse files
committed
Optimize StringUtils.replace/deleteAny for common no-op case
Issue: SPR-15430 (cherry picked from commit ce4eff3)
1 parent 93fac88 commit 865953f

File tree

2 files changed

+55
-9
lines changed

2 files changed

+55
-9
lines changed

spring-core/src/main/java/org/springframework/util/StringUtils.java

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ public static boolean hasText(CharSequence str) {
138138
if (!hasLength(str)) {
139139
return false;
140140
}
141+
141142
int strLen = str.length();
142143
for (int i = 0; i < strLen; i++) {
143144
if (!Character.isWhitespace(str.charAt(i))) {
@@ -172,6 +173,7 @@ public static boolean containsWhitespace(CharSequence str) {
172173
if (!hasLength(str)) {
173174
return false;
174175
}
176+
175177
int strLen = str.length();
176178
for (int i = 0; i < strLen; i++) {
177179
if (Character.isWhitespace(str.charAt(i))) {
@@ -202,6 +204,7 @@ public static String trimWhitespace(String str) {
202204
if (!hasLength(str)) {
203205
return str;
204206
}
207+
205208
StringBuilder sb = new StringBuilder(str);
206209
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
207210
sb.deleteCharAt(0);
@@ -223,6 +226,7 @@ public static String trimAllWhitespace(String str) {
223226
if (!hasLength(str)) {
224227
return str;
225228
}
229+
226230
int len = str.length();
227231
StringBuilder sb = new StringBuilder(str.length());
228232
for (int i = 0; i < len; i++) {
@@ -244,6 +248,7 @@ public static String trimLeadingWhitespace(String str) {
244248
if (!hasLength(str)) {
245249
return str;
246250
}
251+
247252
StringBuilder sb = new StringBuilder(str);
248253
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
249254
sb.deleteCharAt(0);
@@ -261,6 +266,7 @@ public static String trimTrailingWhitespace(String str) {
261266
if (!hasLength(str)) {
262267
return str;
263268
}
269+
264270
StringBuilder sb = new StringBuilder(str);
265271
while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {
266272
sb.deleteCharAt(sb.length() - 1);
@@ -278,6 +284,7 @@ public static String trimLeadingCharacter(String str, char leadingCharacter) {
278284
if (!hasLength(str)) {
279285
return str;
280286
}
287+
281288
StringBuilder sb = new StringBuilder(str);
282289
while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) {
283290
sb.deleteCharAt(0);
@@ -295,6 +302,7 @@ public static String trimTrailingCharacter(String str, char trailingCharacter) {
295302
if (!hasLength(str)) {
296303
return str;
297304
}
305+
298306
StringBuilder sb = new StringBuilder(str);
299307
while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) {
300308
sb.deleteCharAt(sb.length() - 1);
@@ -320,6 +328,7 @@ public static boolean startsWithIgnoreCase(String str, String prefix) {
320328
if (str.length() < prefix.length()) {
321329
return false;
322330
}
331+
323332
String lcStr = str.substring(0, prefix.length()).toLowerCase();
324333
String lcPrefix = prefix.toLowerCase();
325334
return lcStr.equals(lcPrefix);
@@ -374,6 +383,7 @@ public static int countOccurrencesOf(String str, String sub) {
374383
if (!hasLength(str) || !hasLength(sub)) {
375384
return 0;
376385
}
386+
377387
int count = 0;
378388
int pos = 0;
379389
int idx;
@@ -396,19 +406,29 @@ public static String replace(String inString, String oldPattern, String newPatte
396406
if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {
397407
return inString;
398408
}
399-
StringBuilder sb = new StringBuilder();
400-
int pos = 0; // our position in the old string
401409
int index = inString.indexOf(oldPattern);
402-
// the index of an occurrence we've found, or -1
410+
if (index == -1) {
411+
// no occurrence -> can return input as-is
412+
return inString;
413+
}
414+
415+
int capacity = inString.length();
416+
if (newPattern.length() > oldPattern.length()) {
417+
capacity += 16;
418+
}
419+
StringBuilder sb = new StringBuilder(capacity);
420+
421+
int pos = 0; // our position in the old string
403422
int patLen = oldPattern.length();
404423
while (index >= 0) {
405424
sb.append(inString.substring(pos, index));
406425
sb.append(newPattern);
407426
pos = index + patLen;
408427
index = inString.indexOf(oldPattern, pos);
409428
}
429+
430+
// append any characters to the right of a match
410431
sb.append(inString.substring(pos));
411-
// remember to append any characters to the right of a match
412432
return sb.toString();
413433
}
414434

@@ -433,7 +453,8 @@ public static String deleteAny(String inString, String charsToDelete) {
433453
if (!hasLength(inString) || !hasLength(charsToDelete)) {
434454
return inString;
435455
}
436-
StringBuilder sb = new StringBuilder();
456+
457+
StringBuilder sb = new StringBuilder(inString.length());
437458
for (int i = 0; i < inString.length(); i++) {
438459
char c = inString.charAt(i);
439460
if (charsToDelete.indexOf(c) == -1) {
@@ -516,6 +537,7 @@ private static String changeFirstCharacterCase(String str, boolean capitalize) {
516537
if (!hasLength(str)) {
517538
return str;
518539
}
540+
519541
char baseChar = str.charAt(0);
520542
char updatedChar;
521543
if (capitalize) {
@@ -527,6 +549,7 @@ private static String changeFirstCharacterCase(String str, boolean capitalize) {
527549
if (baseChar == updatedChar) {
528550
return str;
529551
}
552+
530553
char[] chars = str.toCharArray();
531554
chars[0] = updatedChar;
532555
return new String(chars, 0, chars.length);
@@ -542,6 +565,7 @@ public static String getFilename(String path) {
542565
if (path == null) {
543566
return null;
544567
}
568+
545569
int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);
546570
return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);
547571
}
@@ -556,14 +580,17 @@ public static String getFilenameExtension(String path) {
556580
if (path == null) {
557581
return null;
558582
}
583+
559584
int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
560585
if (extIndex == -1) {
561586
return null;
562587
}
588+
563589
int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
564590
if (folderIndex > extIndex) {
565591
return null;
566592
}
593+
567594
return path.substring(extIndex + 1);
568595
}
569596

@@ -578,14 +605,17 @@ public static String stripFilenameExtension(String path) {
578605
if (path == null) {
579606
return null;
580607
}
608+
581609
int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
582610
if (extIndex == -1) {
583611
return path;
584612
}
613+
585614
int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
586615
if (folderIndex > extIndex) {
587616
return path;
588617
}
618+
589619
return path.substring(0, extIndex);
590620
}
591621

@@ -701,8 +731,10 @@ public static Locale parseLocaleString(String localeString) {
701731
String[] parts = tokenizeToStringArray(localeString, "_ ", false, false);
702732
String language = (parts.length > 0 ? parts[0] : "");
703733
String country = (parts.length > 1 ? parts[1] : "");
734+
704735
validateLocalePart(language);
705736
validateLocalePart(country);
737+
706738
String variant = "";
707739
if (parts.length > 2) {
708740
// There is definitely a variant, and it is everything after the country
@@ -770,6 +802,7 @@ public static String[] addStringToArray(String[] array, String str) {
770802
if (ObjectUtils.isEmpty(array)) {
771803
return new String[] {str};
772804
}
805+
773806
String[] newArr = new String[array.length + 1];
774807
System.arraycopy(array, 0, newArr, 0, array.length);
775808
newArr[array.length] = str;
@@ -791,6 +824,7 @@ public static String[] concatenateStringArrays(String[] array1, String[] array2)
791824
if (ObjectUtils.isEmpty(array2)) {
792825
return array1;
793826
}
827+
794828
String[] newArr = new String[array1.length + array2.length];
795829
System.arraycopy(array1, 0, newArr, 0, array1.length);
796830
System.arraycopy(array2, 0, newArr, array1.length, array2.length);
@@ -814,6 +848,7 @@ public static String[] mergeStringArrays(String[] array1, String[] array2) {
814848
if (ObjectUtils.isEmpty(array2)) {
815849
return array1;
816850
}
851+
817852
List<String> result = new ArrayList<String>();
818853
result.addAll(Arrays.asList(array1));
819854
for (String str : array2) {
@@ -833,6 +868,7 @@ public static String[] sortStringArray(String[] array) {
833868
if (ObjectUtils.isEmpty(array)) {
834869
return new String[0];
835870
}
871+
836872
Arrays.sort(array);
837873
return array;
838874
}
@@ -848,6 +884,7 @@ public static String[] toStringArray(Collection<String> collection) {
848884
if (collection == null) {
849885
return null;
850886
}
887+
851888
return collection.toArray(new String[collection.size()]);
852889
}
853890

@@ -862,6 +899,7 @@ public static String[] toStringArray(Enumeration<String> enumeration) {
862899
if (enumeration == null) {
863900
return null;
864901
}
902+
865903
List<String> list = Collections.list(enumeration);
866904
return list.toArray(new String[list.size()]);
867905
}
@@ -876,6 +914,7 @@ public static String[] trimArrayElements(String[] array) {
876914
if (ObjectUtils.isEmpty(array)) {
877915
return new String[0];
878916
}
917+
879918
String[] result = new String[array.length];
880919
for (int i = 0; i < array.length; i++) {
881920
String element = array[i];
@@ -894,6 +933,7 @@ public static String[] removeDuplicateStrings(String[] array) {
894933
if (ObjectUtils.isEmpty(array)) {
895934
return array;
896935
}
936+
897937
Set<String> set = new LinkedHashSet<String>();
898938
for (String element : array) {
899939
set.add(element);
@@ -918,6 +958,7 @@ public static String[] split(String toSplit, String delimiter) {
918958
if (offset < 0) {
919959
return null;
920960
}
961+
921962
String beforeDelimiter = toSplit.substring(0, offset);
922963
String afterDelimiter = toSplit.substring(offset + delimiter.length());
923964
return new String[] {beforeDelimiter, afterDelimiter};
@@ -958,6 +999,7 @@ public static Properties splitArrayElementsIntoProperties(
958999
if (ObjectUtils.isEmpty(array)) {
9591000
return null;
9601001
}
1002+
9611003
Properties result = new Properties();
9621004
for (String element : array) {
9631005
if (charsToDelete != null) {
@@ -1018,6 +1060,7 @@ public static String[] tokenizeToStringArray(
10181060
if (str == null) {
10191061
return null;
10201062
}
1063+
10211064
StringTokenizer st = new StringTokenizer(str, delimiters);
10221065
List<String> tokens = new ArrayList<String>();
10231066
while (st.hasMoreTokens()) {
@@ -1071,6 +1114,7 @@ public static String[] delimitedListToStringArray(String str, String delimiter,
10711114
if (delimiter == null) {
10721115
return new String[] {str};
10731116
}
1117+
10741118
List<String> result = new ArrayList<String>();
10751119
if ("".equals(delimiter)) {
10761120
for (int i = 0; i < str.length(); i++) {
@@ -1132,6 +1176,7 @@ public static String collectionToDelimitedString(Collection<?> coll, String deli
11321176
if (CollectionUtils.isEmpty(coll)) {
11331177
return "";
11341178
}
1179+
11351180
StringBuilder sb = new StringBuilder();
11361181
Iterator<?> it = coll.iterator();
11371182
while (it.hasNext()) {
@@ -1178,6 +1223,7 @@ public static String arrayToDelimitedString(Object[] arr, String delim) {
11781223
if (arr.length == 1) {
11791224
return ObjectUtils.nullSafeToString(arr[0]);
11801225
}
1226+
11811227
StringBuilder sb = new StringBuilder();
11821228
for (int i = 0; i < arr.length; i++) {
11831229
if (i > 0) {

spring-core/src/test/java/org/springframework/util/StringUtilsTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -177,15 +177,15 @@ public void testReplace() throws Exception {
177177

178178
// Non match: no change
179179
s = StringUtils.replace(inString, "qwoeiruqopwieurpoqwieur", newPattern);
180-
assertTrue("Replace non matched is equal", s.equals(inString));
180+
assertSame("Replace non-matched is returned as-is", inString, s);
181181

182182
// Null new pattern: should ignore
183183
s = StringUtils.replace(inString, oldPattern, null);
184-
assertTrue("Replace non matched is equal", s.equals(inString));
184+
assertSame("Replace non-matched is returned as-is", inString, s);
185185

186186
// Null old pattern: should ignore
187187
s = StringUtils.replace(inString, null, newPattern);
188-
assertTrue("Replace non matched is equal", s.equals(inString));
188+
assertSame("Replace non-matched is returned as-is", inString, s);
189189
}
190190

191191
@Test

0 commit comments

Comments
 (0)