ผลการดำเนินงานที่ชาญฉลาด parseInt
และช่างเลวร้ายยิ่งกว่าโซลูชันอื่น ๆ เพราะอย่างน้อยต้องมีการจัดการข้อยกเว้น
ฉันรันการทดสอบ jmh และพบว่าการวนซ้ำสตริงโดยใช้ charAt
และการเปรียบเทียบ chars กับ chars ที่ขอบเขตเป็นวิธีที่เร็วที่สุดในการทดสอบว่าสตริงมีเฉพาะตัวเลขหรือไม่
การทดสอบ JMH
การทดสอบเปรียบเทียบประสิทธิภาพของCharacter.isDigit
vs Pattern.matcher().matches
vsLong.parseLong
vs การตรวจสอบค่าถ่าน
วิธีเหล่านี้สามารถสร้างผลลัพธ์ที่แตกต่างกันสำหรับสตริงและสตริงที่มีเครื่องหมาย +/-
การทดสอบทำงานในโหมดกำลังการผลิต ( ดีกว่าดีกว่า ) ด้วยการวอร์มอัพ 5 ครั้งและการทดสอบซ้ำ 5 ครั้ง
ผล
โปรดทราบว่าparseLong
ช้ากว่าisDigit
การทดสอบครั้งแรกเกือบ 100 เท่า
## 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
ชุดทดสอบ
@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;
}
}
}
}
}
}
อัปเดตเมื่อวันที่ 23 กุมภาพันธ์ 2018
- เพิ่มอีกสองกรณี - กรณีหนึ่งใช้
charAt
แทนการสร้างอาร์เรย์พิเศษและอีกกรณีใช้IntStream
รหัสถ่าน
- เพิ่มตัวแบ่งทันทีหากพบตัวเลขที่ไม่ใช่ตัวเลขสำหรับกรณีทดสอบแบบลูป
- ส่งคืน false สำหรับสตริงว่างสำหรับกรณีทดสอบที่วนลูป
อัปเดตเมื่อวันที่ 23 กุมภาพันธ์ 2018
- เพิ่มอีกหนึ่งกรณีทดสอบ (เร็วที่สุด!) ที่เปรียบเทียบค่าถ่านโดยไม่ต้องใช้สตรีม
matches("\\d{2,}")
หรือลองกับPattern
และMatcher