Derek Peirce
11/19/2025, 6:13 AMfun foo(s: String): String {
return "bar" + s.substring(1, 7)
}
becomes in bytecode:
public static final String foo(@NotNull String s) {
Intrinsics.checkNotNullParameter(s, "s");
StringBuilder var10000 = (new StringBuilder()).append("bar");
String var10001 = s.substring(1, 7);
Intrinsics.checkNotNullExpressionValue(var10001, "substring(...)");
return var10000.append(var10001).toString();
}
Ideally, this would instead become:
public static final String foo(@NotNull String s) {
Intrinsics.checkNotNullParameter(s, "s");
StringBuilder var10000 = (new StringBuilder()).append("bar").append(s, 1, 7).toString();
}
to eliminate one string creation and a nullability check on a method that shouldn't need it.JP Sugarbroad
11/19/2025, 9:37 PMDerek Peirce
11/19/2025, 11:54 PM@Benchmark
public String buildStringDirect() {
StringBuilder builder = new StringBuilder();
Random random = new Random(153);
for (int k = 0; k < 500; k++) {
builder.append(string, random.nextInt(50), 50 + random.nextInt(100));
}
return builder.toString();
}
@Benchmark
public String buildStringWithSubstrings() {
StringBuilder builder = new StringBuilder();
Random random = new Random(153);
for (int k = 0; k < 500; k++) {
builder.append(string.substring(random.nextInt(50), 50 + random.nextInt(100)));
}
return builder.toString();
}
with string containing arbitrary lorem ipsum, and got these results:
Benchmark Mode Cnt Score Error Units
StringBenchmark.buildStringDirect avgt 10 24.915 ± 0.290 us/op
StringBenchmark.buildStringWithSubstrings avgt 10 28.771 ± 0.507 us/op
so the substring calls add roughly +15% overhead in this case.ephemient
11/20/2025, 4:19 AMephemient
11/20/2025, 4:25 AMString.subSequence() could return a CharSequence representing a similar non-copying slice (java.lang.String doesn't, but several projects have their own wrappers that do, such as https://github.com/JetBrains/intellij-community/blob/master/platform/util/base/multiplatform/src/com/intellij/util/text/CharSequenceSubSequence.kt)Derek Peirce
11/21/2025, 8:50 PMString.subSequence specifies that it behaves exactly the same as String.substring, so I don't expect that to change, either. Either way, replacing the substring call with a more selective append call should be more performant for everyone. (I can already get the performance improvement by manually replacing the + calls with buildString, but the vast majority of programmers will not even be aware that they'd get an improvement, so the best place for this improvement would be as Kotlin compiles the concatenation.)ephemient
11/22/2025, 3:02 AM