String.charAt vs StringBuilder.charAt performance
Brett Okken
brett.okken.os at gmail.com
Sat Jul 19 19:33:58 UTC 2025
I was looking at the performance of StringCharBuffer for various
backing CharSequence types and was surprised to see a significant
performance difference between String and StringBuffer. I wrote a
small jmh which shows that the String implementation of charAt is
significantly slower than StringBuilder. Is this expected?
Benchmark (data) (source) Mode Cnt
Score Error Units
CharSequenceCharAtBenchmark.test ascii String avgt 3
2537.311 ┬▒ 8952.197 ns/op
CharSequenceCharAtBenchmark.test ascii StringBuffer avgt 3
852.004 ┬▒ 2532.958 ns/op
CharSequenceCharAtBenchmark.test non-ascii String avgt 3
5115.381 ┬▒ 13822.592 ns/op
CharSequenceCharAtBenchmark.test non-ascii StringBuffer avgt 3
836.230 ┬▒ 1154.191 ns/op
@Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 2, time = 7, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Fork(value = 1, jvmArgsPrepend = {"-Xms512M", "-Xmx512M"})
public class CharSequenceCharAtBenchmark {
@Param(value = {"ascii", "non-ascii"})
public String data;
@Param(value = {"String", "StringBuffer"})
public String source;
private CharSequence sequence;
@Setup(Level.Trial)
public void setup() throws Exception {
StringBuilder sb = new StringBuilder(3152);
for (int i=0; i<3152; ++i) {
char c = (char) i;
if ("ascii".equals(data)) {
c = (char) (i & 0x7f);
}
sb.append(c);
}
switch(source) {
case "String":
sequence = sb.toString();
break;
case "StringBuffer":
sequence = sb;
break;
default:
throw new IllegalArgumentException(source);
}
}
@Benchmark
public int test() {
int sum = 0;
for (int i=0, j=sequence.length(); i<j; ++i) {
sum += sequence.charAt(i);
}
return sum;
}
}
More information about the core-libs-dev
mailing list