2009-09-23 5 views
37

아주 간단한 질문이지만 C/C++ 사용자가 자바의 복잡함을 겪고 있습니다.String replaceAll() 대 Matcher replaceAll() (성능 차이)

필자는 jUnit과 몇 가지 성능 테스트를 통해 내 답변을 얻을 수 있음을 알고 있습니다. 그러나 이것이 저쪽에 있는지 궁금합니다.

String.replaceAll()과 Matcher.replaceAll() (Regex.Pattern에서 만든 Matcher 객체) 사이에 성능면에서 알려진 차이가 있습니까?

또한 상위 API의 차이점은 무엇입니까?

답변

62

String.replaceAll의 문서에 따르면, 상기 방법은 호출에 대해 말할 다음 가지고 다음 식 정확히 같은 결과를 산출

형태 str.replaceAll(regex, repl) 의 메소드 호출을

Pattern.compile(regex).matcher(str).replaceAll(repl) 

따라서, String.replaceAll 및 명시 적으로 MatcherPattern을 만들어야합니다.

편집

코멘트에 지적 된 바와 같이 하나가 여러 번 호출을 수행 할 필요가있는 경우, 존재 인 성능 차이는, 그러나, String 또는 Matcher에서 replaceAll에 단일 통화에 대한 사실 것 replaceAll으로 변경하면 컴파일 된 Pattern을 유지하는 것이 좋을 것으로 기대되므로 비교적 비싼 정규식 패턴 컴파일을 매번 수행 할 필요가 없습니다.

+8

넘어 갈 수있는 주된 이유입니다. 만약 당신이 상수 정규식을 사용하고, 그것을 컴파일하고 정적 상수에 막대기. – james

+2

끝에있는 "따라서"의견은 통화 1 건에만 적용됩니다.이 경우 성능 메트릭은 실제로 관련이 없습니다. 동일한 정규 표현식으로 replaceAll을 반복적으로 호출하는 경우 String.replaceAll은 컴파일 된 패턴을 캐싱하는 것보다 느립니다. –

+0

좋은 점 감사합니다. 나는 대답을 편집했습니다. – coobird

3

String.replaceAll의 구현은 당신이 알아야 할 모든 것을 알려줍니다 (불변성, 커피 등 만들기, 빈 문자열 처리, 널 (null)을 처리) : (. 그리고 워드 프로세서 같은 말을)

return Pattern.compile(regex).matcher(this).replaceAll(replacement); 

캐싱을 확인하지는 않았지만 확실히 번으로 컴파일하고 정적 참조를 유지하면 매번 같은 패턴으로 Pattern.compile을 호출하는 것보다 효율적입니다. 캐시가 있다면 효율성을 절약 할 수 있습니다. 그렇지 않은 경우 큰 데이터가 될 수 있습니다.

9

가장 큰 차이점은 Matcher을 생성하는 데 사용 된 Pattern을 잡고 있으면 사용할 때마다 정규식을 다시 컴파일하지 않아도된다는 것입니다. String을 통해 가면 이처럼 "캐시"할 수 없습니다.

매번 다른 정규식을 사용하는 경우 String 클래스의 replaceAll을 사용하는 것이 좋습니다. 많은 문자열에 동일한 정규식을 적용하는 경우 Pattern을 하나 만들고 다시 사용하십시오.String.replaceAll()

+1

내가 이미 말했던 것을 반복하기 위해 대답을 패치하는 것은 절름발이 다. – erickson

+0

그것이 어떤 이유로 든 나를 겨냥했다면, 나는 당신이 대답을 올렸을 때 이미 편집하고 있었다고 생각합니다 ... –

+0

사실, 그것은 coobird를 겨냥한 것입니다. – erickson

20

소스 코드 :

그것은 먼저 패턴을 컴파일하는
public String replaceAll(String regex, String replacement) { 
    return Pattern.compile(regex).matcher(this).replaceAll(replacement); 
} 

- 당신이 그것을 짧은 문자열에서 동일한 패턴으로 여러 번 실행하는 거라면, 성능이 다시 사용할 경우 훨씬 더 좋을 것이다 하나의 패턴을 컴파일합니다.

6

불변성/스레드 안전성 : 컴파일 된 패턴은 변경 불가능하며, Matchers는 변경되지 않습니다. 빈 문자열을 처리

을 (Is Java Regex Thread Safe? 참조) 완전히 대체하기는 정상적으로 빈 문자열을 처리해야합니다 (빈 입력 문자열의 패턴과 일치하지 않습니다)

커피를 만드는 등 : 마지막으로 내가들은, 어느 문자열이나 패턴도 Matcher를을 이를위한 API 기능이 있습니다.

편집 : NULL을 처리 할 때 String 및 Pattern에 대한 설명서에서 명시 적으로 그렇게 말하지 않지만 String을 예상하므로 NullPointerException을 던질 것으로 생각됩니다.

3

차이점은 String.replaceAll()은 호출 될 때마다 정규식을 컴파일한다는 점입니다. 컴파일 된 정규식을 자동으로 캐시하는 .NET의 정적 Regex.Replace() 메소드에는 해당하지 않습니다. 일반적으로 replaceAll()은 한 번만하는 것이지만 동일한 정규 표현식, 특히 반복문에서 반복적으로 호출하려는 경우 Pattern 객체를 만들어 Matcher 메서드를 사용해야합니다.

당신도 미리 일치 프로그램을 만들고 각 사용하기 위해 목표를 재설정하기 위해 reset() 메소드를 사용할 수 있습니다

:

Matcher m = Pattern.compile(regex).matcher(""); 
for (String s : targets) 
{ 
    System.out.println(m.reset(s).replaceAll(repl)); 
} 

물론, Matcher를 재사용의 성능 향상이 같이 아무데도 그 패턴을 재사용하는 것만 큼 훌륭합니다.

0

다른 답변은 OP의 성능 부분을 충분히 다루지 만, Matcher::replaceAllString::replaceAll 사이의 다른 차이점은 사용자 자신의 Pattern을 컴파일하는 이유이기도합니다. Pattern을 직접 컴파일하면 정규식 적용 방법을 수정하는 플래그와 같은 옵션이 있습니다. 예를 들어 :

Pattern myPattern = Pattern.compile(myRegex, Pattern.CASE_INSENSITIVE); 

MatcherMatcher::replaceAll를 호출 할 때 사용자가 설정 한 모든 플래그를 적용합니다.

설정할 수있는 다른 플래그가 있습니다. 대부분 그냥 PatternMatcher API는 많은 옵션을 가지고 있음을 지적하고 싶어하고, 아래의 패턴 compilition의 성능 저하를 언급 한 바와 같이 그 제외하고 간단한 String::replaceAll