Côté performance parseInt
et autres sont bien pires que les autres solutions, car elles nécessitent au moins une gestion des exceptions.
J'ai exécuté des tests jmh et j'ai constaté que l'itération sur String en utilisant charAt
et en comparant les caractères avec les caractères de limite est le moyen le plus rapide de tester si la chaîne ne contient que des chiffres.
Test JMH
Les tests comparent les performances de Character.isDigit
vs Pattern.matcher().matches
vsLong.parseLong
vs en vérifiant les valeurs char.
Ces méthodes peuvent produire des résultats différents pour les chaînes non ascii et les chaînes contenant des signes +/-.
Les tests s'exécutent en mode de débit ( plus il vaut mieux, mieux c'est ) avec 5 itérations d'échauffement et 5 itérations de test.
Résultats
Notez que parseLong
c'est presque 100 fois plus lent que isDigit
pour la première charge de test.
## 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
Suite de tests
@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;
}
}
}
}
}
}
Mis à jour le 23 févr.2018
- Ajoutez deux autres cas - un en utilisant
charAt
au lieu de créer un tableau supplémentaire et un autre en utilisantIntStream
des codes de caractères
- Ajouter une interruption immédiate si aucun chiffre n'est trouvé pour les cas de test en boucle
- Renvoie false pour une chaîne vide pour les cas de test en boucle
Mis à jour le 23 févr.2018
- Ajoutez un autre cas de test (le plus rapide!) Qui compare la valeur char sans utiliser de flux
matches("\\d{2,}")
ou essayez avec unPattern
etMatcher