Uno que conozco es aplicar un Matcher a una subcadena, otro es solo realizar una búsqueda manual para que el primer carácter no esté en minúsculas.
Entonces, ¿cuáles son las buenas maneras de hacer esto?
Usar un Matcher parece ser una gran exageración para una tarea tan simple.
De hecho, es posible usar un método equivalente a C++ find_first_not_of() , pero parece que Java no tiene uno en su biblioteca.
Bueno, la forma en que Matcher lo hace es de 4 a 10 veces más lenta que hacerlo manualmente, aunque, si se hace correctamente, es agradable y legible (vea la respuesta de Andreas).
Sin embargo, al estar acostumbrado a la idea de que el código generalmente debería ser tan eficiente como pueda hacerlo (manteniéndolo legible), todavía estoy interesado en ver si es posible hacer lo mismo más rápido usando la biblioteca estándar y/o trucos nativos de Java. .
Encontré un método idiomático que es un poco más rápido: stream API
Así que los métodos actualmente descubiertos son:
// 1. Andreas' correction of the Matcher way static String getLowercaseSubstringAt (final String s, final int pos) { Matcher m = Pattern.compile("^\\p{Ll}+").matcher(s).region(pos, s.length()); return (m.find() ? m.group() : ""); } // 2. The same with a static Pattern: 2.5 times faster // I thought the compiler would optimize it by default // Don't like the idea to make the Pattern external static Pattern p = Pattern.compile("^\\p{Ll}+"); static String getLowercaseSubstringAt (final String s, final int pos) { Matcher m = p.matcher(s).region(pos, s.length()); return (m.find() ? m.group() : ""); } // 3. Stream API with a range of indices: 3x the speed of the first // Makes use of the Java way to say `find_first(_not)_of()` static String getLowercaseSubstringAt (final String s, final int pos) { int idx = IntStream.range(pos, s.length()) .filter(i -> !Character.isLowerCase(s.charAt(i))) .findFirst() .orElse(s.length()); return s.substring(pos, idx); } // 4. Doing it manually. Relatively dirty, but fast. 10x the speed static String getLowercaseSubstringAt (final String s, final int pos) { for (int i = pos; i < s.length(); i++) if (!Character.isLowerCase(s.charAt(i))) return s.substring(pos, i); return s.substring(pos); }Stream API parece la mejor opción en términos de idioma actualmente: sin necesidad de recursos externos, limpio, relativamente rápido.
No sé por qué dice que la forma de Matcher es realmente un desperdicio, tanto en código como en tiempo.
Claro, una expresión regular es más lenta que un ciclo for simple, pero a menos que esté haciendo esto repetidamente en un ciclo cerrado (más de 1000 veces), no notará la diferencia. Hasta que mida el rendimiento y vea un problema, es probable que no sea un problema. Cuidado con las optimizaciones prematuras.
¿Pero un desperdicio en código? Solo porque lo estás haciendo mal:
static String getLowercaseSubstringAt (final String s, final int pos) { Matcher m = Pattern.compile("^\\p{Ll}+").matcher(s).region(pos, s.length()); return (m.find() ? m.group() : ""); } Dado que su expresión no regular estaba usando la verificación completa en minúsculas con Character.isLowerCase() , actualicé la expresión regular para hacer eso también.