Kinerja-bijaksana parseInt
dan semacamnya jauh lebih buruk daripada solusi lain, karena setidaknya memerlukan penanganan pengecualian.
Saya telah menjalankan tes jmh dan telah menemukan bahwa iterasi dari String menggunakan charAt
dan membandingkan karakter dengan karakter batas adalah cara tercepat untuk menguji apakah string hanya berisi angka.
Pengujian JMH
Tes membandingkan kinerja nilai char Character.isDigit
vs Pattern.matcher().matches
vs Long.parseLong
vs.
Cara-cara ini dapat menghasilkan hasil yang berbeda untuk string non-ascii dan string yang mengandung tanda +/-.
Tes dijalankan dalam mode Throughput ( lebih besar lebih baik ) dengan 5 iterasi pemanasan dan 5 iterasi tes.
Hasil
Perhatikan bahwa parseLong
hampir 100 kali lebih lambat daripada isDigit
untuk beban uji pertama.
## 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 uji
@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;
}
}
}
}
}
}
Diperbarui pada 23 Feb 2018
- Tambahkan dua case lagi - satu menggunakan
charAt
alih-alih membuat array tambahan dan menggunakan IntStream
kode char lainnya
- Tambahkan istirahat segera jika non-digit ditemukan untuk kasus uji berulang
- Mengembalikan false untuk string kosong untuk kasus uji berulang
Diperbarui pada 23 Feb 2018
- Tambahkan satu lagi test case (tercepat!) Yang membandingkan nilai char tanpa menggunakan stream
matches("\\d{2,}")
atau coba denganPattern
danMatcher