빠른 구현 : 활용 String.regionMatches()
정규 표현식을 사용하면 상대적으로 느릴 수 있습니다. 한 가지 경우 만 확인하려는 경우 (느리게) 중요하지 않습니다. 그러나 배열이나 수천 또는 수십만 개의 문자열 모음이 있으면 상황이 상당히 느려질 수 있습니다.
아래 제시된 솔루션은 정규 표현식을 사용하지 않으며 toLowerCase()
(또한 다른 문자열을 만들고 검사 후에 버릴 수 있기 때문에 느립니다).
이 솔루션은 알 수없는 String.regionMatches () 메서드를 기반으로합니다 . 두 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()
하고 미리 캐시 된 소문자 하위 문자열로 호출 합니다. 이 솔루션은 사전 정의 된 하위 문자열을 테스트하기 때문에 아직 유연하지 않습니다.
- 정규 표현식 사용 (허용 된 답변
Pattern.compile().matcher().find()
...)
- 정규 표현식하지만 사전이 생성되고 캐시로 사용
Pattern
. 이 솔루션은 미리 정의 된 하위 문자열을 테스트하기 때문에 아직 유연하지 않습니다.
결과 (메소드를 1,000 만 회 호출) :
- 우리의 방법 : 670ms
- 2 배 toLowerCase () 및 contains () : 2829ms
- 1x toLowerCase () 및 캐시 된 서브 스트링이있는 contains () : 2446ms
- 정규식 : 7180ms
- 캐시 된 정규 표현식
Pattern
: 1845ms
테이블 결과 :
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
우리의 방법은 소문자 및 사용에 비해 4 배 빠르며contains()
, 정규 표현식을 사용할 때보 다 10 배 빠르며 사전 캐시 된 경우 에도 3 배 빠릅니다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");
}
}