The method: ```fun foo(s: String): String { re...
# language-proposals
d
The method:
Copy code
fun foo(s: String): String {
    return "bar" + s.substring(1, 7)
}
becomes in bytecode:
Copy code
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:
Copy code
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.
j
How do those two perform on a JVM? Wouldn't be surprised to find that it's optimized by the runtime.
d
I ran a benchmark:
Copy code
@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:
Copy code
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.
e
in Java 1 until 6, Strings were implemented as slices into a backing char array and substring would reuse the same char array, just with different start/end values. that caused unexpected memory leaks for many programs (https://bugs.java.com/bugdatabase/view_bug?bug_id=4513622) but would have been faster for your pattern here
that's probably not coming back, it would be even more complex with https://openjdk.org/jeps/254, but hypothetically
String.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)