Pod względem wydajności parseInt
i takie są znacznie gorsze niż inne rozwiązania, ponieważ przynajmniej wymagają obsługi wyjątków.
Przeprowadziłem testy jmh i odkryłem, że iteracja ciągów przy użyciu charAt
i porównywanie znaków z znakami granicznymi jest najszybszym sposobem sprawdzenia, czy łańcuch zawiera tylko cyfry.
Testy JMH
Testy porównują wydajność Character.isDigit
vs Pattern.matcher().matches
vs Long.parseLong
vs sprawdzanie wartości char.
Te sposoby mogą dawać różne wyniki dla łańcuchów niepasujących i łańcuchów zawierających znaki +/-.
Testy działają w trybie przepustowości (im większa, tym lepiej ) z 5 iteracjami rozgrzewania i 5 iteracjami testów.
Wyniki
Należy pamiętać, że parseLong
jest prawie 100 razy wolniej niż isDigit
na pierwszym obciążeniu testowym.
## 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
Zestaw testowy
@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;
}
}
}
}
}
}
Zaktualizowano 23 lutego 2018 r
- Dodaj jeszcze dwa przypadki - jeden używa
charAt
zamiast tworzyć dodatkową tablicę, a drugi używa IntStream
kodów char
- Dodaj natychmiastową przerwę, jeśli nie znaleziono cyfr dla zapętlonych przypadków testowych
- Zwraca false dla pustego łańcucha dla zapętlonych przypadków testowych
Zaktualizowano 23 lutego 2018 r
- Dodaj jeszcze jeden przypadek testowy (najszybszy!), Który porównuje wartość char bez użycia strumienia
matches("\\d{2,}")
spróbuj albo z -Pattern
iMatcher