From e9d7afbe2bf667167614869c7ea10e91f96ffceb Mon Sep 17 00:00:00 2001 From: Edgar Asatryan <17509127+nstdio@users.noreply.github.com> Date: Wed, 4 Mar 2020 18:11:23 +0400 Subject: [PATCH] [LANG-1523] Avoid unnecessary allocation in wrapIfMissing. (#496) Now `StringUtils#wrapIfMissing(String, char)` and `StringUtils#wrapIfMissing(String, String)` does not allocate buffer when input is already wrapped. --- .../org/apache/commons/lang3/StringUtils.java | 27 +++++++++++++++---- .../apache/commons/lang3/StringUtilsTest.java | 8 ++++-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/commons/lang3/StringUtils.java b/src/main/java/org/apache/commons/lang3/StringUtils.java index 2668731eb..5c31d7bcd 100644 --- a/src/main/java/org/apache/commons/lang3/StringUtils.java +++ b/src/main/java/org/apache/commons/lang3/StringUtils.java @@ -9448,6 +9448,8 @@ public class StringUtils { * Wraps a string with a char if that char is missing from the start or end of the given string. *
* + *A new {@code String} will not be created if {@code str} is already wrapped.
+ * ** StringUtils.wrapIfMissing(null, *) = null * StringUtils.wrapIfMissing("", *) = "" @@ -9472,12 +9474,18 @@ public class StringUtils { if (isEmpty(str) || wrapWith == CharUtils.NUL) { return str; } + final boolean wrapStart = str.charAt(0) != wrapWith; + final boolean wrapEnd = str.charAt(str.length() - 1) != wrapWith; + if (!wrapStart && !wrapEnd) { + return str; + } + final StringBuilder builder = new StringBuilder(str.length() + 2); - if (str.charAt(0) != wrapWith) { + if (wrapStart) { builder.append(wrapWith); } builder.append(str); - if (str.charAt(str.length() - 1) != wrapWith) { + if (wrapEnd) { builder.append(wrapWith); } return builder.toString(); @@ -9488,6 +9496,8 @@ public class StringUtils { * Wraps a string with a string if that string is missing from the start or end of the given string. * * + *A new {@code String} will not be created if {@code str} is already wrapped.
+ * ** StringUtils.wrapIfMissing(null, *) = null * StringUtils.wrapIfMissing("", *) = "" @@ -9508,7 +9518,7 @@ public class StringUtils { * @param str * the string to be wrapped, may be {@code null} * @param wrapWith - * the char that will wrap {@code str} + * the string that will wrap {@code str} * @return the wrapped string, or {@code null} if {@code str==null} * @since 3.5 */ @@ -9516,12 +9526,19 @@ public class StringUtils { if (isEmpty(str) || isEmpty(wrapWith)) { return str; } + + final boolean wrapStart = !str.startsWith(wrapWith); + final boolean wrapEnd = !str.endsWith(wrapWith); + if (!wrapStart && !wrapEnd) { + return str; + } + final StringBuilder builder = new StringBuilder(str.length() + wrapWith.length() + wrapWith.length()); - if (!str.startsWith(wrapWith)) { + if (wrapStart) { builder.append(wrapWith); } builder.append(str); - if (!str.endsWith(wrapWith)) { + if (wrapEnd) { builder.append(wrapWith); } return builder.toString(); diff --git a/src/test/java/org/apache/commons/lang3/StringUtilsTest.java b/src/test/java/org/apache/commons/lang3/StringUtilsTest.java index e7eb5f075..30263de6b 100644 --- a/src/test/java/org/apache/commons/lang3/StringUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/StringUtilsTest.java @@ -3237,7 +3237,9 @@ public class StringUtilsTest { assertEquals("/x/y/z/", StringUtils.wrapIfMissing("x/y/z", '/')); assertEquals("/x/y/z/", StringUtils.wrapIfMissing("/x/y/z", '/')); assertEquals("/x/y/z/", StringUtils.wrapIfMissing("x/y/z/", '/')); - assertEquals("/", StringUtils.wrapIfMissing("/", '/')); + + assertSame("/", StringUtils.wrapIfMissing("/", '/')); + assertSame("/x/", StringUtils.wrapIfMissing("/x/", '/')); } @Test @@ -3259,7 +3261,9 @@ public class StringUtilsTest { assertEquals("/x/y/z/", StringUtils.wrapIfMissing("x/y/z/", "/")); assertEquals("/", StringUtils.wrapIfMissing("/", "/")); assertEquals("ab/ab", StringUtils.wrapIfMissing("/", "ab")); - assertEquals("ab/ab", StringUtils.wrapIfMissing("ab/ab", "ab")); + + assertSame("ab/ab", StringUtils.wrapIfMissing("ab/ab", "ab")); + assertSame("//x//", StringUtils.wrapIfMissing("//x//", "//")); } @Test