Thực hiện nhanh hơn: Sử dụng String.regionMatches()
Sử dụng regrec có thể tương đối chậm. Nó (chậm) không thành vấn đề nếu bạn chỉ muốn kiểm tra trong một trường hợp. Nhưng nếu bạn có một mảng hoặc một bộ sưu tập hàng ngàn hoặc hàng trăm ngàn chuỗi, mọi thứ có thể trở nên khá chậm.
Giải pháp được trình bày dưới đây không sử dụng các biểu thức thông thường cũng không toLowerCase()
(cũng chậm vì nó tạo ra một chuỗi khác và chỉ ném chúng đi sau khi kiểm tra).
Giải pháp xây dựng trên phương thức String.regionMatches () dường như chưa biết. Nó kiểm tra xem 2 String
vùng có khớp nhau không, nhưng điều quan trọng là nó cũng có tình trạng quá tải với một ignoreCase
tham số tiện dụng .
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;
}
Phân tích tốc độ
Phân tích tốc độ này không có nghĩa là khoa học tên lửa, chỉ là một bức tranh sơ bộ về tốc độ của các phương pháp khác nhau.
Tôi so sánh 5 phương pháp.
- Phương thức chứaIgnoreCase () của chúng tôi .
- Bằng cách chuyển đổi cả hai chuỗi thành chữ thường và cuộc gọi
String.contains()
.
- Bằng cách chuyển đổi chuỗi nguồn thành chữ thường và gọi
String.contains()
với chuỗi con được lưu trước, được lưu trong bộ đệm. Giải pháp này đã không linh hoạt vì nó kiểm tra một chuỗi con tiền thân.
- Sử dụng biểu thức chính quy (câu trả lời được chấp nhận
Pattern.compile().matcher().find()
...)
- Sử dụng biểu thức chính quy nhưng được tạo trước và lưu trữ
Pattern
. Giải pháp này đã không linh hoạt vì nó kiểm tra một chuỗi con được xác định trước.
Kết quả (bằng cách gọi phương thức 10 triệu lần):
- Phương pháp của chúng tôi: 670 ms
- 2x toLowerCase () và chứa (): 2829 ms
- 1x toLowerCase () và chứa () với chuỗi con được lưu trong bộ nhớ cache: 2446 ms
- Regapi: 7180 ms
- Regrec với bộ nhớ cache
Pattern
: 1845 ms
Kết quả trong một bảng:
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
Phương pháp của chúng tôi nhanh hơn gấp 4 lần so với sử dụng và hạ thấp contains()
, nhanh hơn gấp 10 lần so với sử dụng biểu thức thông thường và cũng nhanh hơn gấp 3 lần ngay cả khi Pattern
được lưu trước bộ đệm (và mất tính linh hoạt khi kiểm tra chuỗi con tùy ý).
Phân tích mã kiểm tra
Nếu bạn quan tâm đến cách phân tích được thực hiện, đây là ứng dụng hoàn chỉnh có thể chạy được:
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");
}
}