2012-01-27 3 views
0

하여 Goolge 전에 그 일이 comparisions의 많은 아니라에 온다 ​​무엇인지 내가 무엇을 찾고 있어요 :자바 : 빠른 루프에서 분할 또는

내가 스플릿 문자열

을 반복 할 경우 더
String[] flagArr = flags.split(";"); 
for (String f: flagArr) { 
    // some stuff 
} 

또는 컴파일러가 분할을 할 똑똑 궁금 코드의 두 번째 파이스 동전으로

for (String f: flags.split(";")) { 
    // some stuff 
} 

한 번만

+2

왜 스스로 간단한 벤치 마크를 실행하지 않습니까? 동일한 문자열을 n 번 (n이 충분히 큰) n 번 실행하고 관심있는 모든 시간이나 메모리 사용량을 실행하십시오. – pintxo

+0

시간을 측정하는 벤치 마크에 대해 생각했습니다. 그러나 Java가 내 프로그래밍 기술을 향상시키는 것에 대한 지식을 더 많이 습득하기 때문에 질문을하는 것이 더 나은 선택이라고 생각했습니다. 또한 다른 사람들도 같은 질문을 할 수 있습니다. –

+0

두 버전을 모두 디버깅 할 수 있습니다. _back stage_가 어떻게되는지 알아내는 것입니다. –

답변

5

생성 된 바이트 코드를 확인할 수는 있지만 둘 다 똑같은 일을 할 것이라고 확신합니다. 왜 두 번째 것이 다른 것일까 요?

편집 : 알다시피 두 가지 방법으로 split() 번만 호출하십시오.

다음은 첫 번째에 대한 바이트 코드 : 두 번째에 대한

public class javatesting.JavaTesting extends java.lang.Object{ 
public javatesting.JavaTesting(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public static void main(java.lang.String[]); 
    Code: 
    0: ldc  #2; //String 1;2;3 
    2: astore_1 
    3: aload_1 
    4: ldc  #3; //String ; 
    6: invokevirtual #4; //Method java/lang/String.split:(Ljava/lang/String;) 
[Ljava/lang/String; 
    9: astore_2 
    10: aload_2 
    11: astore_3 
    12: aload_3 
    13: arraylength 
    14: istore 4 
    16: iconst_0 
    17: istore 5 
    19: iload 5 
    21: iload 4 
    23: if_icmpge  41 
    26: aload_3 
    27: iload 5 
    29: aaload 
    30: astore 6 
    32: aconst_null 
    33: astore 6 
    35: iinc 5, 1 
    38: goto 19 
    41: return 

} 

그리고 바이트 코드 :

public class javatesting.JavaTesting extends java.lang.Object{ 
public javatesting.JavaTesting(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public static void main(java.lang.String[]); 
    Code: 
    0: ldc  #2; //String 1;2;3 
    2: astore_1 
    3: aload_1 
    4: ldc  #3; //String ; 
    6: invokevirtual #4; //Method java/lang/String.split:(Ljava/lang/String;) 
[Ljava/lang/String; 
    9: astore_2 
    10: aload_2 
    11: arraylength 
    12: istore_3 
    13: iconst_0 
    14: istore 4 
    16: iload 4 
    18: iload_3 
    19: if_icmpge  37 
    22: aload_2 
    23: iload 4 
    25: aaload 
    26: astore 5 
    28: aconst_null 
    29: astore 5 
    31: iinc 4, 1 
    34: goto 16 
    37: return 

} 
+0

감사합니다. 정확하게 질문에 답변했습니다. 그게 내가 찾고 있던거야. –

0

I 생각해 보면 컴파일러가이를 최적화합니다. 선택한 Java 공급자 (sun, ibm, etc ...)에 따라 달라질 수 있지만 모든 컴파일러가 그렇게하지 않으면 너무 간단합니다.

실제로 매우 큰 flags 문자열을 만들고 간단한 성능 테스트를 수행 하시겠습니까?

그러나이 용이하기 때문에, 당신의 첫 번째 옵션을하지 왜 ....

5

그것은 전혀 문제가되지 않습니다. 이러한 유형의 최적화로 시간을 낭비하지 마십시오.

EDIT : 분할과 배열이 for 루프 범위에만 있기 때문에 두 번째 옵션을 사용하는 것이 좋습니다.

+0

전적으로 동의하지 않습니다. 다른 방향으로 어떤 것을하면 성능이 크게 달라질 수 있습니다. 예를 들면 다음 링크를 참조하십시오 : http://www.roseindia.net/javatutorials/appending_strings.shtml –

+0

예, 때로는 중요하고 때로는 그렇지 않습니다. 이 경우에는 그렇지 않으며 그것이 내 요점이었습니다. –

0

분할이 하나만 수행된다는 것은 확실합니다. 내부적으로이 코드는 반복자가있는 루프로 변환되므로 컴파일러는 한 번 분할을 수행하고 결과 콜렉션을 작성한 다음 해당 콜렉션에 대한 반복자를 작성합니다.

반복을 반복하면 iterator가 올바른 위치를 가리 키지 않습니다.

0

이 시도 : 당신이 정말보고 싶다면

public double calculateRunTime() 
{ 
    long startTime = System.currentTimeMillis(); 

    split(); 

    long endTime = System.currentTimeMillis(); 

    return endTime - startTime; 
} 
3

을 바이트 코드의 차이는 javap -c입니다.

public static void test(java.lang.String); 
    Code: 
    0: aload_0 
    1: ldC#16; //String ; 
    3: invokevirtual #18; //Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 
    6: astore_1 
    7: aload_1 
    8: dup 
    9: astore 5 
    11: arraylength 
    12: istore 4 
    14: iconst_0 
    15: istore_3 
    16: goto 27 
    19: aload 5 
    21: iload_3 
    22: aaload 
    23: astore_2 
    24: iinc 3, 1 
    27: iload_3 
    28: iload 4 
    30: if_icmplt 19 
    33: return 
} 

둘째 : 이클립스 컴파일러로 컴파일 (소스 코드에서 지역 변수와) 첫 번째 버전이 제공

public static void test(java.lang.String); 
    Code: 
    0: aload_0 
    1: ldC#16; //String ; 
    3: invokevirtual #18; //Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String; 
    6: dup 
    7: astore 4 
    9: arraylength 
    10: istore_3 
    11: iconst_0 
    12: istore_2 
    13: goto 24 
    16: aload 4 
    18: iload_2 
    19: aaload 
    20: astore_1 
    21: iinc 2, 1 
    24: iload_2 
    25: iload_3 
    26: if_icmplt 16 
    29: return 
} 

당신이이 글을 읽을 수 있다면 당신은 (볼 수 있듯이), 첫 번째 변형은 추가 로컬 변수 슬롯 (처음에는 astore_1/aload_1)을 사용합니다.그러나 (a) 바이트 코드가 해석 되더라도 오버 헤드는 무시할 수 있습니다 (참조를 복사하는 것입니다). 그리고 (b) JIT 컴파일러는 미리 정적 바이트 코드 최적화를 수행했는지 여부에 상관없이 최적화 할 수 있습니다. : 지역 변수 (1)는 이후에 사용되지 않습니다.

소스 코드의 로컬 변수는 주로 중간 결과를 명확하게하고 재사용 할 수 있도록 사용됩니다. 기본적으로 명시적인 지역 변수가없는 두 번째 변형은 split의 결과를 사용자의 메서드에서 (또는 루프 내에서 직접) 다시 사용할 수 없도록하는 반면 코드에서 로컬 변수의 이름을 지정할 수는 있습니다. 내가 컴파일러가 똑똑하면 충분히 가 분할을 할 궁금 코드의 두 번째 파이스 동전으로

한 번만

당신의 두 가지 방법 사이의 유일한 차이는에서 지역 변수의 명시 적 선언이다 소스 코드. 두 경우

, for (String f: flags.split(";")) { }은 문법적 (즉 제외하고는하지도 않는다 로컬 변수가 액세스)에 상당 :

이 표기법
  • , _hidden_i은 참고하기

    int _hidden_i = 0; 
    for (String[] _hidden_arr=flags.split(";"); _hidden_i<_hidden_arr.length; _hidden_i++){ 
        String f = _hidden_arr[_hidden_i]; 
        // some stuff 
    } 
    

    점한다는 것이다 노출되지 않으므로 무언가를 _hidden_arr[_hidden_i]에 할당 할 수 없습니다.

  • 순진한 소스 - 바이트 코드 컴파일러조차도 _hidden_arr_hidden_i이 결코 다시 사용되지 않을 것이므로 더 이상 사용하기 위해 복사하지 않습니다. 근본적으로 달라졌을 것이다 무엇

는이었을 것입니다 :

for (var i = 0; i < flags.split(";").length; i++) { 
    String f = flags.split(";")[i]; 
} 

for(;;) 표기법에서, 첫 번째 부분은 항상 한 번 실행됩니다. 두 번째 표현식은 루프 블록에 들어가기 전에 항상 실행됩니다 (실행 여부를 테스트하기 위해). 마지막은 루프 블록을 실행 한 후에 항상 실행됩니다 (break이 사용되지 않는 한).

일반적으로 두 번째 부분에 무거운 할 수있는 메서드 호출은 피하고 싶습니다. 특히 항상 같은 결과가 나오는 경우가 많습니다.

+1

감사합니다 - 아주 좋은 배경 지식. –