En cuanto al rendimiento parseInt
, son mucho peores que otras soluciones, porque al menos requieren un manejo de excepciones.
He ejecutado pruebas jmh y he descubierto que iterar sobre String usando charAt
y comparando caracteres con caracteres de límite es la forma más rápida de probar si la cadena contiene solo dígitos.
Prueba de JMH
Las pruebas comparan el rendimiento de Character.isDigit
vs Pattern.matcher().matches
vs Long.parseLong
vs comprobación de valores de caracteres.
Estas formas pueden producir resultados diferentes para cadenas no ascii y cadenas que contienen signos +/-.
Las pruebas se ejecutan en modo de rendimiento ( mayor es mejor ) con 5 iteraciones de calentamiento y 5 iteraciones de prueba.
Resultados
Tenga en cuenta que parseLong
es casi 100 veces más lento que isDigit
para la primera carga de prueba.
## Test load with 25% valid strings (75% strings contain non-digit symbols)
Benchmark Mode Cnt Score Error Units
testIsDigit thrpt 5 9.275 ± 2.348 ops/s
testPattern thrpt 5 2.135 ± 0.697 ops/s
testParseLong thrpt 5 0.166 ± 0.021 ops/s
## Test load with 50% valid strings (50% strings contain non-digit symbols)
Benchmark Mode Cnt Score Error Units
testCharBetween thrpt 5 16.773 ± 0.401 ops/s
testCharAtIsDigit thrpt 5 8.917 ± 0.767 ops/s
testCharArrayIsDigit thrpt 5 6.553 ± 0.425 ops/s
testPattern thrpt 5 1.287 ± 0.057 ops/s
testIntStreamCodes thrpt 5 0.966 ± 0.051 ops/s
testParseLong thrpt 5 0.174 ± 0.013 ops/s
testParseInt thrpt 5 0.078 ± 0.001 ops/s
Banco de pruebas
@State(Scope.Benchmark)
public class StringIsNumberBenchmark {
private static final long CYCLES = 1_000_000L;
private static final String[] STRINGS = {"12345678901","98765432177","58745896328","35741596328", "123456789a1", "1a345678901", "1234567890 "};
private static final Pattern PATTERN = Pattern.compile("\\d+");
@Benchmark
public void testPattern() {
for (int i = 0; i < CYCLES; i++) {
for (String s : STRINGS) {
boolean b = false;
b = PATTERN.matcher(s).matches();
}
}
}
@Benchmark
public void testParseLong() {
for (int i = 0; i < CYCLES; i++) {
for (String s : STRINGS) {
boolean b = false;
try {
Long.parseLong(s);
b = true;
} catch (NumberFormatException e) {
// no-op
}
}
}
}
@Benchmark
public void testCharArrayIsDigit() {
for (int i = 0; i < CYCLES; i++) {
for (String s : STRINGS) {
boolean b = false;
for (char c : s.toCharArray()) {
b = Character.isDigit(c);
if (!b) {
break;
}
}
}
}
}
@Benchmark
public void testCharAtIsDigit() {
for (int i = 0; i < CYCLES; i++) {
for (String s : STRINGS) {
boolean b = false;
for (int j = 0; j < s.length(); j++) {
b = Character.isDigit(s.charAt(j));
if (!b) {
break;
}
}
}
}
}
@Benchmark
public void testIntStreamCodes() {
for (int i = 0; i < CYCLES; i++) {
for (String s : STRINGS) {
boolean b = false;
b = s.chars().allMatch(c -> c > 47 && c < 58);
}
}
}
@Benchmark
public void testCharBetween() {
for (int i = 0; i < CYCLES; i++) {
for (String s : STRINGS) {
boolean b = false;
for (int j = 0; j < s.length(); j++) {
char charr = s.charAt(j);
b = '0' <= charr && charr <= '9';
if (!b) {
break;
}
}
}
}
}
}
Actualizado el 23 de febrero de 2018
- Agregue dos casos más: uno usando en
charAt
lugar de crear una matriz adicional y otro usando IntStream
códigos char
- Agregue un descanso inmediato si no se encuentran dígitos para los casos de prueba en bucle
- Devuelve falso para cadena vacía para casos de prueba en bucle
Actualizado el 23 de febrero de 2018
- Agregue un caso de prueba más (¡el más rápido!) Que compara el valor de char sin usar stream
matches("\\d{2,}")
o intente con unaPattern
yMatcher