การใช้งานที่เร็วขึ้น: การใช้ประโยชน์ String.regionMatches()
การใช้ regexp นั้นค่อนข้างช้า (ช้า) มันไม่สำคัญว่าคุณแค่ต้องการเช็คอินหนึ่งกรณี แต่ถ้าคุณมีอาเรย์หรือชุดของพันหรือร้อยเป็นพันของสตริงสิ่งต่าง ๆ อาจช้าลงได้
โซลูชันที่นำเสนอด้านล่างไม่ใช้นิพจน์ทั่วไปหรือtoLowerCase()
(ซึ่งช้าเช่นกันเพราะสร้างสตริงใหม่และเพิ่งโยนทิ้งหลังจากเช็ค)
โซลูชันสร้างบนเมธอด String.regionMatches ()ซึ่งดูเหมือนจะไม่เป็นที่รู้จัก มันตรวจสอบว่า 2 String
ภูมิภาคตรงกันหรือไม่ แต่ที่สำคัญคือมันมีโอเวอร์โหลดพร้อมignoreCase
พารามิเตอร์ที่ใช้งานสะดวก
public static boolean containsIgnoreCase(String src, String what) {
final int length = what.length();
if (length == 0)
return true; // Empty string is contained
final char firstLo = Character.toLowerCase(what.charAt(0));
final char firstUp = Character.toUpperCase(what.charAt(0));
for (int i = src.length() - length; i >= 0; i--) {
// Quick check before calling the more expensive regionMatches() method:
final char ch = src.charAt(i);
if (ch != firstLo && ch != firstUp)
continue;
if (src.regionMatches(true, i, what, 0, length))
return true;
}
return false;
}
การวิเคราะห์ความเร็ว
การวิเคราะห์ความเร็วนี้ไม่ได้หมายถึงวิทยาศาสตร์จรวด แต่เป็นเพียงภาพคร่าวๆของวิธีการที่แตกต่างกันอย่างรวดเร็ว
ฉันเปรียบเทียบ 5 วิธี
- เราcontainsIgnoreCase ()วิธีการ
String.contains()
โดยการแปลงสตริงทั้งสองจะลดกรณีและโทร
- โดยการแปลงสตริงซอร์สให้เป็นตัวพิมพ์เล็กและโทร
String.contains()
ด้วยสตริงย่อยที่แคชไว้ล่วงหน้าและต่ำกว่า วิธีนี้ไม่ยืดหยุ่นเท่าที่ควรเพราะจะทำการทดสอบ substring ที่ต้องการ
- ใช้การแสดงออกปกติ (คำตอบที่ยอมรับ
Pattern.compile().matcher().find()
... )
- โดยใช้การแสดงออกปกติ
Pattern
แต่มีการสร้างไว้ล่วงหน้าและเก็บไว้ชั่วคราว วิธีการแก้ปัญหานี้มีความยืดหยุ่นไม่ได้เพราะมันทดสอบ substring ที่กำหนดไว้ล่วงหน้า
ผลลัพธ์ (โดยเรียกวิธีการ 10 ล้านครั้ง):
- วิธีการของเรา: 670 มิลลิวินาที
- 2x toLowerCase () และมี (): 2829 ms
- 1x toLowerCase () และประกอบด้วย () พร้อมสตริงย่อยที่เก็บไว้: 2446 ms
- Regexp: 7180 มิลลิวินาที
- Regexp ด้วยแคช
Pattern
: 1845 ms
ผลลัพธ์ในตาราง:
RELATIVE SPEED 1/RELATIVE SPEED
METHOD EXEC TIME TO SLOWEST TO FASTEST (#1)
------------------------------------------------------------------------------
1. Using regionMatches() 670 ms 10.7x 1.0x
2. 2x lowercase+contains 2829 ms 2.5x 4.2x
3. 1x lowercase+contains cache 2446 ms 2.9x 3.7x
4. Regexp 7180 ms 1.0x 10.7x
5. Regexp+cached pattern 1845 ms 3.9x 2.8x
วิธีการของเราคือ4x เร็วขึ้นเมื่อเทียบกับ lowercasing และการใช้contains()
, 10x ได้เร็วขึ้นเมื่อเทียบกับการใช้นิพจน์ปกติและยัง3x เร็วขึ้นแม้ว่าPattern
เป็นแคชล่วงหน้า (และการสูญเสียความยืดหยุ่นของการตรวจสอบการย่อยโดยพล)
รหัสทดสอบการวิเคราะห์
หากคุณสนใจว่าจะทำการวิเคราะห์อย่างไรต่อไปนี้เป็นแอปพลิเคชันที่รันได้สมบูรณ์แบบ:
import java.util.regex.Pattern;
public class ContainsAnalysis {
// Case 1 utilizing String.regionMatches()
public static boolean containsIgnoreCase(String src, String what) {
final int length = what.length();
if (length == 0)
return true; // Empty string is contained
final char firstLo = Character.toLowerCase(what.charAt(0));
final char firstUp = Character.toUpperCase(what.charAt(0));
for (int i = src.length() - length; i >= 0; i--) {
// Quick check before calling the more expensive regionMatches()
// method:
final char ch = src.charAt(i);
if (ch != firstLo && ch != firstUp)
continue;
if (src.regionMatches(true, i, what, 0, length))
return true;
}
return false;
}
// Case 2 with 2x toLowerCase() and contains()
public static boolean containsConverting(String src, String what) {
return src.toLowerCase().contains(what.toLowerCase());
}
// The cached substring for case 3
private static final String S = "i am".toLowerCase();
// Case 3 with pre-cached substring and 1x toLowerCase() and contains()
public static boolean containsConverting(String src) {
return src.toLowerCase().contains(S);
}
// Case 4 with regexp
public static boolean containsIgnoreCaseRegexp(String src, String what) {
return Pattern.compile(Pattern.quote(what), Pattern.CASE_INSENSITIVE)
.matcher(src).find();
}
// The cached pattern for case 5
private static final Pattern P = Pattern.compile(
Pattern.quote("i am"), Pattern.CASE_INSENSITIVE);
// Case 5 with pre-cached Pattern
public static boolean containsIgnoreCaseRegexp(String src) {
return P.matcher(src).find();
}
// Main method: perfroms speed analysis on different contains methods
// (case ignored)
public static void main(String[] args) throws Exception {
final String src = "Hi, I am Adam";
final String what = "i am";
long start, end;
final int N = 10_000_000;
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsIgnoreCase(src, what);
end = System.nanoTime();
System.out.println("Case 1 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsConverting(src, what);
end = System.nanoTime();
System.out.println("Case 2 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsConverting(src);
end = System.nanoTime();
System.out.println("Case 3 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsIgnoreCaseRegexp(src, what);
end = System.nanoTime();
System.out.println("Case 4 took " + ((end - start) / 1000000) + "ms");
start = System.nanoTime();
for (int i = 0; i < N; i++)
containsIgnoreCaseRegexp(src);
end = System.nanoTime();
System.out.println("Case 5 took " + ((end - start) / 1000000) + "ms");
}
}