tuning - java profiler
¿La forma más eficiente de hacer el primer personaje de una minúscula String? (10)
A pesar de un enfoque orientado al carbón, sugeriría una solución orientada a cadenas. String.toLowerCase es específico de la String.toLowerCase regional, por lo que tendría en cuenta este problema. String.toLowerCase
es String.toLowerCase
para mayúsculas y minúsculas de acuerdo con Character.toLowerCase . Además, una solución orientada a caracteres no es compatible con Unicode completo, porque Character.toLowerCase no puede manejar caracteres suplementarios.
public static final String uncapitalize(final String originalStr,
final Locale locale) {
final int splitIndex = 1;
final String result;
if (originalStr.isEmpty()) {
result = originalStr;
} else {
final String first = originalStr.substring(0, splitIndex).toLowerCase(
locale);
final String rest = originalStr.substring(splitIndex);
final StringBuilder uncapStr = new StringBuilder(first).append(rest);
result = uncapStr.toString();
}
return result;
}
ACTUALIZACIÓN: A modo de ejemplo, cuán importante es la configuración regional, permítanos minúsculas I
en turco y alemán:
System.out.println(uncapitalize("I", new Locale("TR","tr")));
System.out.println(uncapitalize("I", new Locale("DE","de")));
dará salida a dos resultados diferentes:
yo
yo
¿Cuál es la forma más eficiente de crear el primer personaje de una minúscula String
?
Puedo pensar en varias formas de hacer esto:
Usar charAt()
con substring()
String input = "SomeInputString";
String output = Character.toLowerCase(input.charAt(0)) +
(input.length() > 1 ? input.substring(1) : "");
O usando una matriz de caracteres
String input = "SomeInputString";
char c[] = input.toCharArray();
c[0] = Character.toLowerCase(c[0]);
String output = new String(c);
Estoy seguro de que hay muchas otras formas excelentes de lograr esto. ¿Que recomiendas?
Cuando se trata de manipulación de cuerdas, eche un vistazo a Jakarta Commons Lang StringUtils .
Encontré una buena alternativa si no quieres usar una biblioteca de terceros:
import java.beans.Introspector;
Assert.assertEquals("someInputString", Introspector.decapitalize("SomeInputString"));
Las cadenas en Java son inmutables, por lo que de cualquier forma se creará una nueva cadena.
Su primer ejemplo probablemente será un poco más eficiente porque solo necesita crear una nueva cadena y no una matriz de caracteres temporales.
Me he encontrado con esto solo hoy. Intenté hacerlo yo mismo de la manera más peatonal. Eso tomó una línea, aunque sea larga. Aquí va
String str = "TaxoRank";
System.out.println(" Before str = " + str);
str = str.replaceFirst(str.substring(0,1), str.substring(0,1).toLowerCase());
System.out.println(" After str = " + str);
Da:
Antes de str = TaxoRanks
Después de str = taxoRanks
Probé los enfoques prometedores usando JMH . code referencia completo.
Suposición durante las pruebas (para evitar verificar los casos de esquina cada vez): la longitud de cadena de entrada siempre es mayor 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
El puntaje es operaciones por segundo, cuanto más, mejor.
Pruebas
test1
fue el primer acercamiento de Andy y Hllink:string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
test2
fue el segundo acercamiento de Andy. También esIntrospector.decapitalize()
sugerido por Daniel, pero sin dos declaracionesif
. Primeroif
se eliminó debido a la suposición de prueba. El segundo fue eliminado, porque violaba la corrección (es decir, la entrada"HI"
devolvería"HI"
). Esto fue casi el más rápido.char c[] = string.toCharArray(); c[0] = Character.toLowerCase(c[0]); string = new String(c);
test3
fue una modificación detest2
, pero en lugar deCharacter.toLowerCase()
, estaba agregando 32, que funciona correctamente si y solo si la cadena está en ASCII. Este fue el más rápido.c[0] |= '' ''
del comment de Mike dio el mismo rendimiento.char c[] = string.toCharArray(); c[0] += 32; string = new String(c);
test4
usóStringBuilder
.StringBuilder sb = new StringBuilder(string); sb.setCharAt(0, Character.toLowerCase(sb.charAt(0))); string = sb.toString();
test5
usó dos llamadas desubstring()
.string = string.substring(0, 1).toLowerCase() + string.substring(1);
test6
usa la reflexión para cambiar elchar value[]
directamente en String. Este fue el más 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(); }
Conclusiones
Si la longitud de cadena es siempre mayor que 0, use test2
.
Si no, tenemos que verificar los casos de esquina:
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);
}
Si está seguro de que su texto estará siempre en ASCII y está buscando un rendimiento extremo porque encontró este código en el cuello de botella, use test3
.
Si desea utilizar Apache Commons, puede hacer lo siguiente:
import org.apache.commons.lang3.text.WordUtils;
[...]
String s = "SomeString";
String firstLower = WordUtils.uncapitalize(s);
Resultado: someString
Si lo que necesita es muy simple (por ejemplo, nombres de clases Java, sin configuraciones regionales), también puede usar la clase CaseFormat en la biblioteca de Google Guava .
String converted = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, "FooBar");
assertEquals("fooBar", converted);
O puede preparar y reutilizar un objeto convertidor, que podría ser más eficiente.
Converter<String, String> converter=
CaseFormat.UPPER_CAMEL.converterTo(CaseFormat.LOWER_CAMEL);
assertEquals("fooBar", converter.convert("FooBar"));
Para comprender mejor la filosofía de la manipulación de cadenas de Google Guava, consulte esta página wiki .
Un método estático muy breve y simple para archivar lo que desea:
public static String decapitalizeString(String string) {
return string == null || string.isEmpty() ? "" : Character.toLowerCase(string.charAt(0)) + string.substring(1);
}
String testString = "SomeInputString";
String firstLetter = testString.substring(0,1).toLowerCase();
String restLetters = testString.substring(1);
String resultString = firstLetter + restLetters;