Skip to content

Commit 6a4979b

Browse files
committed
[LANG-1805] Speedup StringUtils.join(AllPrimitiveTypes[], char, int,
int), see StringUtilsJoinBenchmark. - Better StringBuilder allocation - See StringUtilsJoinBenchmark - Based on #1532
1 parent 8eff0a4 commit 6a4979b

File tree

4 files changed

+4190
-56
lines changed

4 files changed

+4190
-56
lines changed

src/changes/changes.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ The <action> type attribute can be add,update,fix,remove.
8888
<action type="fix" dev="ggregory" due-to="Gary Gregory, sekharchowdary774">Fix Javadoc comma spacing #1538.</action>
8989
<action issue="LANG-1807" type="fix" dev="ggregory" due-to="Zhongxin Yan, Gary Gregory">Make private static variables final in RandomUtils.</action>
9090
<action issue="LANG-1448" type="fix" dev="ggregory" due-to="David W. Burhans, Gary Gregory, kamGP92">Javadoc: Normalize some varargs examples.</action>
91+
<action issue="LANG-1805" type="fix" dev="ggregory" due-to="Gary Gregory, jher235">Speedup StringUtils.join(AllPrimitiveTypes[], char, int, int), see StringUtilsJoinBenchmark, based on #1532. Also better StringBuilder allocation</action>
9192
<!-- ADD -->
9293
<action type="add" dev="ggregory" due-to="Gary Gregory">Add JavaVersion.JAVA_27.</action>
9394
<action type="add" dev="ggregory" due-to="Gary Gregory">Add SystemUtils.IS_JAVA_27.</action>

src/main/java/org/apache/commons/lang3/StringUtils.java

Lines changed: 84 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,18 @@ public static String appendIfMissingIgnoreCase(final String str, final CharSeque
504504
return Strings.CI.appendIfMissing(str, suffix, suffixes);
505505
}
506506

507+
/**
508+
* Computes the capacity required for a StringBuilder to hold {@code items} of {@code maxElementChars} characters plus the separators between them. The
509+
* separator is assumed to be 1 character.
510+
*
511+
* @param count The number of items.
512+
* @param maxElementChars The maximum number of characters per item.
513+
* @return A StringBuilder with the appropriate capacity.
514+
*/
515+
private static StringBuilder capacity(final int count, final byte maxElementChars) {
516+
return new StringBuilder(count * maxElementChars + count - 1);
517+
}
518+
507519
/**
508520
* Capitalizes a String changing the first character to title case as per {@link Character#toTitleCase(int)}. No other characters are changed.
509521
*
@@ -3854,19 +3866,21 @@ public static String join(final boolean[] array, final char delimiter) {
38543866
* @since 3.12.0
38553867
*/
38563868
public static String join(final boolean[] array, final char delimiter, final int startIndex, final int endIndex) {
3869+
// See StringUtilsJoinBenchmark
38573870
if (array == null) {
38583871
return null;
38593872
}
3860-
if (endIndex - startIndex <= 0) {
3873+
final int count = endIndex - startIndex;
3874+
if (count <= 0) {
38613875
return EMPTY;
38623876
}
3863-
final StringBuilder stringBuilder = new StringBuilder(array.length * 5 + array.length - 1);
3864-
for (int i = startIndex; i < endIndex; i++) {
3865-
stringBuilder
3866-
.append(array[i])
3867-
.append(delimiter);
3877+
final byte maxElementChars = 5; // "false"
3878+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
3879+
stringBuilder.append(array[startIndex]);
3880+
for (int i = startIndex + 1; i < endIndex; i++) {
3881+
stringBuilder.append(delimiter).append(array[i]);
38683882
}
3869-
return stringBuilder.substring(0, stringBuilder.length() - 1);
3883+
return stringBuilder.toString();
38703884
}
38713885

38723886
/**
@@ -3929,19 +3943,21 @@ public static String join(final byte[] array, final char delimiter) {
39293943
* @since 3.2
39303944
*/
39313945
public static String join(final byte[] array, final char delimiter, final int startIndex, final int endIndex) {
3946+
// See StringUtilsJoinBenchmark
39323947
if (array == null) {
39333948
return null;
39343949
}
3935-
if (endIndex - startIndex <= 0) {
3950+
final int count = endIndex - startIndex;
3951+
if (count <= 0) {
39363952
return EMPTY;
39373953
}
3938-
final StringBuilder stringBuilder = new StringBuilder();
3939-
for (int i = startIndex; i < endIndex; i++) {
3940-
stringBuilder
3941-
.append(array[i])
3942-
.append(delimiter);
3954+
final byte maxElementChars = 4; // "-128"
3955+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
3956+
stringBuilder.append(array[startIndex]);
3957+
for (int i = startIndex + 1; i < endIndex; i++) {
3958+
stringBuilder.append(delimiter).append(array[i]);
39433959
}
3944-
return stringBuilder.substring(0, stringBuilder.length() - 1);
3960+
return stringBuilder.toString();
39453961
}
39463962

39473963
/**
@@ -4004,19 +4020,21 @@ public static String join(final char[] array, final char delimiter) {
40044020
* @since 3.2
40054021
*/
40064022
public static String join(final char[] array, final char delimiter, final int startIndex, final int endIndex) {
4023+
// See StringUtilsJoinBenchmark
40074024
if (array == null) {
40084025
return null;
40094026
}
4010-
if (endIndex - startIndex <= 0) {
4027+
final int count = endIndex - startIndex;
4028+
if (count <= 0) {
40114029
return EMPTY;
40124030
}
4013-
final StringBuilder stringBuilder = new StringBuilder(array.length * 2 - 1);
4014-
for (int i = startIndex; i < endIndex; i++) {
4015-
stringBuilder
4016-
.append(array[i])
4017-
.append(delimiter);
4031+
final byte maxElementChars = 1;
4032+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
4033+
stringBuilder.append(array[startIndex]);
4034+
for (int i = startIndex + 1; i < endIndex; i++) {
4035+
stringBuilder.append(delimiter).append(array[i]);
40184036
}
4019-
return stringBuilder.substring(0, stringBuilder.length() - 1);
4037+
return stringBuilder.toString();
40204038
}
40214039

40224040
/**
@@ -4079,19 +4097,21 @@ public static String join(final double[] array, final char delimiter) {
40794097
* @since 3.2
40804098
*/
40814099
public static String join(final double[] array, final char delimiter, final int startIndex, final int endIndex) {
4100+
// See StringUtilsJoinBenchmark
40824101
if (array == null) {
40834102
return null;
40844103
}
4085-
if (endIndex - startIndex <= 0) {
4104+
final int count = endIndex - startIndex;
4105+
if (count <= 0) {
40864106
return EMPTY;
40874107
}
4088-
final StringBuilder stringBuilder = new StringBuilder();
4089-
for (int i = startIndex; i < endIndex; i++) {
4090-
stringBuilder
4091-
.append(array[i])
4092-
.append(delimiter);
4108+
final byte maxElementChars = 22; // "1.7976931348623157E308"
4109+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
4110+
stringBuilder.append(array[startIndex]);
4111+
for (int i = startIndex + 1; i < endIndex; i++) {
4112+
stringBuilder.append(delimiter).append(array[i]);
40934113
}
4094-
return stringBuilder.substring(0, stringBuilder.length() - 1);
4114+
return stringBuilder.toString();
40954115
}
40964116

40974117
/**
@@ -4154,19 +4174,21 @@ public static String join(final float[] array, final char delimiter) {
41544174
* @since 3.2
41554175
*/
41564176
public static String join(final float[] array, final char delimiter, final int startIndex, final int endIndex) {
4177+
// See StringUtilsJoinBenchmark
41574178
if (array == null) {
41584179
return null;
41594180
}
4160-
if (endIndex - startIndex <= 0) {
4181+
final int count = endIndex - startIndex;
4182+
if (count <= 0) {
41614183
return EMPTY;
41624184
}
4163-
final StringBuilder stringBuilder = new StringBuilder();
4164-
for (int i = startIndex; i < endIndex; i++) {
4165-
stringBuilder
4166-
.append(array[i])
4167-
.append(delimiter);
4185+
final byte maxElementChars = 12; // "3.4028235E38"
4186+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
4187+
stringBuilder.append(array[startIndex]);
4188+
for (int i = startIndex + 1; i < endIndex; i++) {
4189+
stringBuilder.append(delimiter).append(array[i]);
41684190
}
4169-
return stringBuilder.substring(0, stringBuilder.length() - 1);
4191+
return stringBuilder.toString();
41704192
}
41714193

41724194
/**
@@ -4229,19 +4251,21 @@ public static String join(final int[] array, final char separator) {
42294251
* @since 3.2
42304252
*/
42314253
public static String join(final int[] array, final char delimiter, final int startIndex, final int endIndex) {
4254+
// See StringUtilsJoinBenchmark
42324255
if (array == null) {
42334256
return null;
42344257
}
4235-
if (endIndex - startIndex <= 0) {
4258+
final int count = endIndex - startIndex;
4259+
if (count <= 0) {
42364260
return EMPTY;
42374261
}
4238-
final StringBuilder stringBuilder = new StringBuilder();
4239-
for (int i = startIndex; i < endIndex; i++) {
4240-
stringBuilder
4241-
.append(array[i])
4242-
.append(delimiter);
4262+
final byte maxElementChars = 11; // "-2147483648"
4263+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
4264+
stringBuilder.append(array[startIndex]);
4265+
for (int i = startIndex + 1; i < endIndex; i++) {
4266+
stringBuilder.append(delimiter).append(array[i]);
42434267
}
4244-
return stringBuilder.substring(0, stringBuilder.length() - 1);
4268+
return stringBuilder.toString();
42454269
}
42464270

42474271
/**
@@ -4467,19 +4491,21 @@ public static String join(final long[] array, final char separator) {
44674491
* @since 3.2
44684492
*/
44694493
public static String join(final long[] array, final char delimiter, final int startIndex, final int endIndex) {
4494+
// See StringUtilsJoinBenchmark
44704495
if (array == null) {
44714496
return null;
44724497
}
4473-
if (endIndex - startIndex <= 0) {
4498+
final int count = endIndex - startIndex;
4499+
if (count <= 0) {
44744500
return EMPTY;
44754501
}
4476-
final StringBuilder stringBuilder = new StringBuilder();
4477-
for (int i = startIndex; i < endIndex; i++) {
4478-
stringBuilder
4479-
.append(array[i])
4480-
.append(delimiter);
4502+
final byte maxElementChars = 20; // "-9223372036854775808"
4503+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
4504+
stringBuilder.append(array[startIndex]);
4505+
for (int i = startIndex + 1; i < endIndex; i++) {
4506+
stringBuilder.append(delimiter).append(array[i]);
44814507
}
4482-
return stringBuilder.substring(0, stringBuilder.length() - 1);
4508+
return stringBuilder.toString();
44834509
}
44844510

44854511
/**
@@ -4661,19 +4687,21 @@ public static String join(final short[] array, final char delimiter) {
46614687
* @since 3.2
46624688
*/
46634689
public static String join(final short[] array, final char delimiter, final int startIndex, final int endIndex) {
4690+
// See StringUtilsJoinBenchmark
46644691
if (array == null) {
46654692
return null;
46664693
}
4667-
if (endIndex - startIndex <= 0) {
4694+
final int count = endIndex - startIndex;
4695+
if (count <= 0) {
46684696
return EMPTY;
46694697
}
4670-
final StringBuilder stringBuilder = new StringBuilder();
4671-
for (int i = startIndex; i < endIndex; i++) {
4672-
stringBuilder
4673-
.append(array[i])
4674-
.append(delimiter);
4698+
final byte maxElementChars = 6; // "-32768"
4699+
final StringBuilder stringBuilder = capacity(count, maxElementChars);
4700+
stringBuilder.append(array[startIndex]);
4701+
for (int i = startIndex + 1; i < endIndex; i++) {
4702+
stringBuilder.append(delimiter).append(array[i]);
46754703
}
4676-
return stringBuilder.substring(0, stringBuilder.length() - 1);
4704+
return stringBuilder.toString();
46774705
}
46784706

46794707
/**

0 commit comments

Comments
 (0)