Testei as abordagens promissoras usando JMH . Código de referência completo .
Suposição durante os testes (para evitar a verificação de casos extremos sempre): o comprimento da string de entrada é sempre maior que 1.
Resultados
Benchmark Mode Cnt Score Error Units
MyBenchmark.test1 thrpt 20 10463220.493 ± 288805.068 ops/s
MyBenchmark.test2 thrpt 20 14730158.709 ± 530444.444 ops/s
MyBenchmark.test3 thrpt 20 16079551.751 ± 56884.357 ops/s
MyBenchmark.test4 thrpt 20 9762578.446 ± 584316.582 ops/s
MyBenchmark.test5 thrpt 20 6093216.066 ± 180062.872 ops/s
MyBenchmark.test6 thrpt 20 2104102.578 ± 18705.805 ops/s
A pontuação é operações por segundo, quanto mais, melhor.
Testes
test1
foi a primeira abordagem de Andy e Hllink:
string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
test2
foi a abordagem do segundo Andy. Também é Introspector.decapitalize()
sugerido por Daniel, mas sem duas if
afirmações. O primeiro if
foi removido devido à suposição de teste. O segundo foi removido, porque estava violando a correção (ou seja, a entrada "HI"
voltaria "HI"
). Isso foi quase o mais rápido.
char c[] = string.toCharArray();
c[0] = Character.toLowerCase(c[0]);
string = new String(c);
test3
foi uma modificação de test2
, mas em vez de Character.toLowerCase()
, eu estava adicionando 32, que funciona corretamente se e somente se a string estiver em ASCII. Este foi o mais rápido. c[0] |= ' '
do comentário de Mike deu o mesmo desempenho.
char c[] = string.toCharArray();
c[0] += 32;
string = new String(c);
test4
usado StringBuilder
.
StringBuilder sb = new StringBuilder(string);
sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
string = sb.toString();
test5
usou duas substring()
chamadas.
string = string.substring(0, 1).toLowerCase() + string.substring(1);
test6
usa reflexão para mudar char value[]
diretamente na String. Este foi o mais lento.
try {
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[]) field.get(string);
value[0] = Character.toLowerCase(value[0]);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
Conclusões
Se o comprimento da string for sempre maior que 0, use test2
.
Caso contrário, temos que verificar os casos mais importantes:
public static String decapitalize(String string) {
if (string == null || string.length() == 0) {
return string;
}
char c[] = string.toCharArray();
c[0] = Character.toLowerCase(c[0]);
return new String(c);
}
Se você tiver certeza de que seu texto estará sempre em ASCII e estiver procurando por desempenho extremo porque encontrou esse código no gargalo, use test3
.