2009-06-16 5 views
6

우리는 거대한 코드베이스를 가지고 있으며, 우리는 StringBuilder/StringBuffer의 사용으로 이익을 얻을 수있는 코드에 "+"기반 문자열 concat가 상당히 있다고 의심합니다. Eclipse를 검색 할 수있는 효과적인 방법이나 기존 도구가 있습니까?큰 Java 코드베이스에서 순진한 ("+"기반) 문자열 연결을 찾는 방법은 무엇입니까?

"+"로 검색하는 것은 좋은 생각이 아닙니다. 코드에 많은 수학이 있기 때문에 코드와 유형을 실제로 분석하여 문자열을 포함하는 추가 항목을 파악해야합니다.

답변

12

실제로 어디인지 정확히 이해하십시오.StringBuilder을 사용하는 것이 더 좋습니다. 난 당신이하지 알 수 있습니까 말하는 게 아니에요하지만,이 같은 코드를 걸릴 것이라고 많은 사람들 확실히있다 :

String foo = "Your age is: " + getAge(); 

과로 돌려는 :

StringBuilder builder = new StringBuilder("Your age is: "); 
builder.append(getAge()); 
String foo = builder.toString(); 

단지를하는 동일한 것을 덜 읽을 수있는 버전. 흔히 순진한 해결책은 최상의 솔루션입니다. 마찬가지로 일부 사람들은 걱정할 필요가 있습니다.

String x = "long line" + 
    "another long line"; 

사실 실제로 해당 연결은 컴파일 타임에 수행됩니다. nsander으로

은 아주 당연히 당신은 문제가 처음 가지고있는 경우에 나는 프로파일 러를 사용하는 것이 좋습니다

+7

정확합니다. 그래서 FindBugs는 루프 만 검사합니다. –

+2

뿐만 아니라 컴파일러 (적어도 javac)는 Concatentation의 대부분을 StringBuffer/StringBuilder.append()로 변환합니다. –

+0

물론 ...그러나 후손을위한 좋은 메모입니다. 컴파일 타임에 최적화 할 수없는 프로그램 방식의 연결 체인 (예 : 루프)이있는 상황이 대부분 걱정됩니다. – Uri

13

나는 FindBugs이이를 감지 할 수 있다고 확신합니다. 그렇지 않다면 주변에 가지고있는 것이 여전히 매우 유용합니다.

편집 : 실제로 차이를 만드는 유일한 시간 인 concatenations in a loop을 찾을 수 있습니다.

+0

+1 루프의 연결에 대해. 그것이 나를 두려워 할 것입니다. – ojrac

+0

나는 대부분 루프에 대해 걱정했다. 그래서 findbugs가 그렇게한다면, 나는 분명히 그것을 줄 것이다. 이전에 findbugs를 다른 프로젝트에서 사용했지만 퍼포먼스는 신경 쓰지 않았습니다. 감사! – Uri

+0

예, FindBugs에는 성능만을위한 여러 범주가 있습니다. 예를 들어 "메서드는 toString을 호출하기위한 박스형 프리미티브를 할당합니다", "명시 적 가비지 수집, 벤치 마크 코드를 제외하고는 매우 모호합니다"및 "entrySet 반복기 대신 keySet 반복기를 비효율적으로 사용"이 있습니다. –

10

프로파일 러를 사용하여 실제로 중요한 "간단한"문자열 연결을 찾으십시오. 실제로 필요한 경우보다 자세한 StringBuffer로만 전환하십시오.

+0

대부분의 경우 StringBuilder로 전환하면됩니다. – Fredrik

2

IntelliJ는 "구조 검색"을 사용하여 찾을 수 있습니다. "$ a + $ b"를 검색하고 $ a와 $ b의 특성을 java.lang.String 유형으로 설정합니다.

그러나 IntelliJ를 사용하는 경우 원하는 검사를 더 잘 수행 할 수있는 검사가 내장되어있을 가능성이 큽니다.

0

PMD를 사용하면 XPath 또는 Java 구문을 사용하여 규칙을 작성할 수 있습니다. 문자열 연결 연산자 —과 일치 할 수 있는지 여부를 조사해 볼 가치가 있습니다. 확실히 정적 분석 범위 내에있는 것 같습니다. 이것은 매우 모호한 아이디어이며, 나는이 "커뮤니티 위키"를 만들 것이다. 다른 사람이이 라인을 따라 정교하게 (또는 자신의 대답을 만들) 원한다면, 제발하십시오! 대신 아마 대다수를 찾을 수 "++" 사람들을 위해 단지 + 검색 검색의

1

. 여러 변수를 연결하는 경우가 더 어려울 것입니다.

+0

대부분의 경우 문자열 변수 사이에 공백을 추가하므로 대부분의 경우 여전히 잡을 수 있습니다. –

+0

또는 선택적 공백을 설명하는 정규식을 사용하십시오. "\"\ s? + " "+ \ s? \ "" –

2

... 발견했다입니다. 이것은 실제 성능 질문이며 합리적인 테스트 데이터로 코드를 표시 할 수 없다면 코드를 변경하는 데 어떤 가치도 없을 것입니다.

0

잊어 버려 - 당신의 JVM은 대부분 이미 수행 - the JLS, 15.18.1.2 Optimization of String Concatenation를 참조하십시오

구현을 만든 다음 중간 String 객체를 폐기 방지하기 위해 한 번에 변환 및 연결을 수행하도록 선택할 수 있습니다. 반복되는 문자열 연결의 성능을 높이려면 Java 컴파일러에서 StringBuffer 클래스 또는 유사한 기술을 사용하여 식의 평가에 의해 생성 된 중간 String 개체의 수를 줄이십시오.

+1

하지만 루프에서는이 작업을 수행 할 수 없습니다. 즉, 무언가를 반복하고 + =를 사용하여 문자열을 작성하는 경우 각 반복마다 새 StringBuffer/Builder가 만들어집니다. –

+0

좋은 점 - JLS에있는 모든 것이 _may_이므로 증거가 없습니다. 나는 시간이있을 때 바이트 코드를 봐야 할 것이다. –

2

존 소총 (항상) 나머지는 이미 모든 것을 필요 말했다하지만 난 정말

봐 ... 어쩌면이 아닌 기존의 성능 향상을 위해 사냥하는 것을 강조하고 싶습니다 이 코드 :

public class StringBuilding { 
    public static void main(String args[]) { 
    String a = "The first part"; 
    String b = "The second part"; 
    String res = a+b; 

    System.gc(); // Inserted to make it easier to see "before" and "after" below 

    res = new StringBuilder().append(a).append(b).toString(); 
    } 
} 

당신이 그것을 컴파일은 javap로 분해하면, 이것은 당신이 무엇을 얻을 수 있습니다.

public static void main(java.lang.String[]); 
    Code: 
    0: ldc  #2; //String The first part 
    2: astore_1 
    3: ldc  #3; //String The second part 
    5: astore_2 
    6: new  #4; //class java/lang/StringBuilder 
    9: dup 
    10: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V 
    13: aload_1 
    14: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    17: aload_2 
    18: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    21: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
    24: astore_3 
    25: invokestatic #8; //Method java/lang/System.gc:()V 
    28: new  #4; //class java/lang/StringBuilder 
    31: dup 
    32: invokespecial #5; //Method java/lang/StringBuilder."<init>":()V 
    35: aload_1 
    36: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    39: aload_2 
    40: invokevirtual #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 
    43: invokevirtual #7; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 
    46: astore_3 
    47: return 

6-21은 28-43과 거의 같습니다. 별로 최적화하지 않았습니까?

편집 : 루프 문제는 유효하지만 ...

1

당신은 아마 또는 "+"연결을 포함하지 않을 수있다 핫스팟을 많이 가지고 거대한 코드베이스가있는 경우. 평범한 프로파일 러를 실행하고 어떤 종류의 구성인지에 관계없이 큰 프로파일을 수정하십시오.

실제 병목 현상을 수정하는 것보다 (잠재적 인) 병목 현상을 해결하는 것은 이상한 방법입니다.

3

performance worse and your code less readable 가능성이 있습니다. 컴파일러는 이미이 최적화를 수행하며, 루프에 있지 않으면 일반적으로 더 나은 작업을 수행합니다. 또한 JDK 8에서는 StringUberBuilder가 제공 될 수 있으며 StringBuilder를 사용하는 모든 코드는 느리게 실행되지만 "+"연결된 문자열은 새 클래스의 이점을 얻습니다.

"조기 최적화는 모든 효율성의 97 %를 차지합니다. 조기 최적화는 모든 악의 뿌리입니다. 그러나 우리는 그 중요한 3 %에서 기회를 포기해서는 안됩니다. "- Donald Knuth

관련 문제