Przetestowałem obiecujące podejścia przy użyciu JMH . Pełny kod testu porównawczego .
Założenie podczas testów (aby uniknąć sprawdzania przypadków narożnych za każdym razem): wejściowa długość ciągu jest zawsze większa niż 1.
Wyniki
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
Wynik to liczba operacji na sekundę, im więcej, tym lepiej.
Testy
test1 było pierwszym podejściem Andy'ego i Hllinka:
string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
test2było drugim podejściem Andy'ego. Introspector.decapitalize()Sugeruje to również Daniel, ale bez dwóch ifstwierdzeń. Pierwszy ifzostał usunięty z powodu założenia testowego. Drugi został usunięty, ponieważ naruszał poprawność (tzn. Wejście "HI"zwracało "HI"). To był prawie najszybszy.
char c[] = string.toCharArray();
c[0] = Character.toLowerCase(c[0]);
string = new String(c);
test3była modyfikacją test2, ale zamiast tego Character.toLowerCase()dodałem 32, które działa poprawnie wtedy i tylko wtedy, gdy łańcuch jest w ASCII. To było najszybsze. c[0] |= ' 'z komentarza Mike'a dał taki sam występ.
char c[] = string.toCharArray();
c[0] += 32;
string = new String(c);
test4używany StringBuilder.
StringBuilder sb = new StringBuilder(string);
sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
string = sb.toString();
test5użył dwóch substring()połączeń.
string = string.substring(0, 1).toLowerCase() + string.substring(1);
test6używa odbicia, aby zmienić char value[]bezpośrednio w ciągu. To było najwolniejsze.
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();
}
Wnioski
Jeśli długość ciągu jest zawsze większa niż 0, użyj test2.
Jeśli nie, musimy sprawdzić narożniki:
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);
}
Jeśli jesteś pewien, że twój tekst będzie zawsze w ASCII i szukasz ekstremalnej wydajności, ponieważ znalazłeś ten kod w wąskim gardle, użyj test3.