Java에서 문자열 연결보다 String.format을 사용하는 것이 더 나은 방법입니까?

StackOverflow https://stackoverflow.com/questions/925423

문제

사용하는 것 사이에 눈에 띄는 차이가 있습니까? String.format 및 Java의 문자열 연결?

나는 사용하는 편이다 String.format 그러나 때때로 미끄러지거나 연결을 사용합니다.하나가 다른 것보다 나은지 궁금합니다.

내가보기 엔, String.format 문자열을 "형식화"하는 데 더 많은 기능을 제공합니다.그리고 연결은 실수로 추가 %s를 넣거나 하나를 누락하는 것에 대해 걱정할 필요가 없음을 의미합니다.

String.format 또한 더 짧습니다.

어느 것이 더 읽기 쉬운지는 머리가 어떻게 작동하는지에 따라 다릅니다.

도움이 되었습니까?

해결책

사용하는 것이 더 나은 연습이라고 제안합니다 String.format(). 주된 이유는 그 이유입니다 String.format() 리소스 파일에서로드 된 텍스트로 더 쉽게 현지화 할 수있는 반면, 각 언어마다 다른 코드로 새로운 실행 파일을 생성하지 않고는 연결을 현지화 할 수 없습니다.

앱을 현지화 할 계획이라면 형식 토큰에 대한 인수 위치를 지정하는 습관도 들어야합니다.

"Hello %1$s the time is %2$t"

그런 다음 다른 순서를 설명하기 위해 실행 파일의 재 컴파일없이 이름과 시간 토큰을 교체 할 수 있습니다. 인수 위치를 사용하면 기능을 두 번 전달하지 않고 동일한 인수를 재사용 할 수도 있습니다.

String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)

다른 팁

성능에 대해 :

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }
  long end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;

  start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = String.format("Hi %s; Hi to you %s",i, + i*2);
  }
  end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
}

타이밍 결과는 다음과 같습니다.

  • 연결 = 265 밀리 초
  • 형식 = 4141 밀리 초

따라서 연결은 string.format보다 훨씬 빠릅니다.

성능에 대한 토론이 있기 때문에 StringBuilder가 포함 된 비교를 추가 할 것이라고 생각했습니다. 실제로는 동의보다 빠르고 자연스럽게 문자열 옵션입니다.

이것을 사과와 사과 비교로 만들기 위해 나는 외부가 아닌 루프에서 새로운 StringBuilder를 인스턴스화합니다 (이것은 실제로 루핑 부문을 위해 공간을 다시 배치하는 오버 헤드로 인해 하나의 인스턴스화를하는 것보다 실제로 더 빠릅니다. 하나의 빌더).

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    log.info("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    log.info("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("; Hi to you ").append(i * 2);
    }

    end = System.currentTimeMillis();

    log.info("String Builder = " + ((end - start)) + " millisecond");
  • 2012-01-11 16 : 30 : 46,058 정보 [testmain]-형식 = 1416 밀리 초
  • 2012-01-11 16 : 30 : 46,190 정보 [testmain]-연결 = 134 밀리 초
  • 2012-01-11 16 : 30 : 46,313 정보 [testmain]-문자열 빌더 = 117 밀리 초

하나의 문제 .format 정적 유형 안전을 잃는 것입니다. 형식에 대한 인수가 너무 적을 수 있으며 형식 지정자에 대한 잘못된 유형을 가질 수 있습니다. 둘 다 IllegalFormatException 런타임에, 따라서 생산을 중단하는 로깅 코드로 끝날 수 있습니다.

대조적으로, 논쟁 + 컴파일러에서 테스트 할 수 있습니다.

더 읽기 쉬운 것은 머리가 어떻게 작동하는지에 달려 있습니다.

당신은 바로 당신의 답변을 얻었습니다.

그것은 개인적인 취향의 문제입니다.

문자열 연결은 약간 빠르지 만 무시할 수 있다고 생각합니다.

다음은 밀리 초의 여러 샘플 크기의 테스트입니다.

public class Time {

public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;

public static void main(String[] args) {

  int i = 1;
  for(int run=1; run <= 12; run++){
      for(int test =1; test <= 2 ; test++){
        System.out.println(
                String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
        test(run, i);
      }
      System.out.println("\n____________________________");
      i = i*3;
  }
}

public static void test(int run, int iterations){

      long start = System.nanoTime();
      for( int i=0;i<iterations; i++){
          String s = "echo " + i + " > "+ sysFile;
      }
      long t = System.nanoTime() - start;   
      String r = String.format("  %-13s =%10d %s", "Concatenation",t,"nanosecond");
      System.out.println(r) ;


     start = System.nanoTime();       
     for( int i=0;i<iterations; i++){
         String s =  String.format(cmdString, i);
     }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "Format",t,"nanosecond");
     System.out.println(r);

      start = System.nanoTime();          
      for( int i=0;i<iterations; i++){
          StringBuilder b = new StringBuilder("echo ");
          b.append(i).append(" > ").append(sysFile);
          String s = b.toString();
      }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "StringBuilder",t,"nanosecond");
     System.out.println(r);
}

}

TEST: 1, RUN: 1, Iterations: 1
  Concatenation =     14911 nanosecond
  Format        =     45026 nanosecond
  StringBuilder =      3509 nanosecond

TEST: 1, RUN: 2, Iterations: 1
  Concatenation =      3509 nanosecond
  Format        =     38594 nanosecond
  StringBuilder =      3509 nanosecond

____________________________

TEST: 2, RUN: 1, Iterations: 3
  Concatenation =      8479 nanosecond
  Format        =     94438 nanosecond
  StringBuilder =      5263 nanosecond

TEST: 2, RUN: 2, Iterations: 3
  Concatenation =      4970 nanosecond
  Format        =     92976 nanosecond
  StringBuilder =      5848 nanosecond

____________________________

TEST: 3, RUN: 1, Iterations: 9
  Concatenation =     11403 nanosecond
  Format        =    287115 nanosecond
  StringBuilder =     14326 nanosecond

TEST: 3, RUN: 2, Iterations: 9
  Concatenation =     12280 nanosecond
  Format        =    209051 nanosecond
  StringBuilder =     11818 nanosecond

____________________________

TEST: 5, RUN: 1, Iterations: 81
  Concatenation =     54383 nanosecond
  Format        =   1503113 nanosecond
  StringBuilder =     40056 nanosecond

TEST: 5, RUN: 2, Iterations: 81
  Concatenation =     44149 nanosecond
  Format        =   1264241 nanosecond
  StringBuilder =     34208 nanosecond

____________________________

TEST: 6, RUN: 1, Iterations: 243
  Concatenation =     76018 nanosecond
  Format        =   3210891 nanosecond
  StringBuilder =     76603 nanosecond

TEST: 6, RUN: 2, Iterations: 243
  Concatenation =     91222 nanosecond
  Format        =   2716773 nanosecond
  StringBuilder =     73972 nanosecond

____________________________

TEST: 8, RUN: 1, Iterations: 2187
  Concatenation =    527450 nanosecond
  Format        =  10291108 nanosecond
  StringBuilder =    885027 nanosecond

TEST: 8, RUN: 2, Iterations: 2187
  Concatenation =    526865 nanosecond
  Format        =   6294307 nanosecond
  StringBuilder =    591773 nanosecond

____________________________

TEST: 10, RUN: 1, Iterations: 19683
  Concatenation =   4592961 nanosecond
  Format        =  60114307 nanosecond
  StringBuilder =   2129387 nanosecond

TEST: 10, RUN: 2, Iterations: 19683
  Concatenation =   1850166 nanosecond
  Format        =  35940524 nanosecond
  StringBuilder =   1885544 nanosecond

  ____________________________

TEST: 12, RUN: 1, Iterations: 177147
  Concatenation =  26847286 nanosecond
  Format        = 126332877 nanosecond
  StringBuilder =  17578914 nanosecond

TEST: 12, RUN: 2, Iterations: 177147
  Concatenation =  24405056 nanosecond
  Format        = 129707207 nanosecond
  StringBuilder =  12253840 nanosecond

다음은 호출 수정과 동일한 테스트입니다. ToString () 방법에 대한 방법 StringBuilder. 아래 결과는 StringBuilder 접근 방식이 + 운영자.

파일 : StringTest.java

class StringTest {

  public static void main(String[] args) {

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("Hi to you ").append(i * 2).toString();
    }

    end = System.currentTimeMillis();

    System.out.println("String Builder = " + ((end - start)) + " millisecond");

  }
}

쉘 명령 : (문자열 테스트 5 번 컴파일 및 실행)

> javac StringTest.java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; java StringTest; done"

결과 :

Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond

Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond

Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond

String.format() 현악기를 연결하는 것 이상입니다. 예를 들어, 사용을 사용하여 특정 로케일에 숫자를 표시 할 수 있습니다. String.format().

그러나 현지화에 신경 쓰지 않으면 기능적 차이가 없습니다. 어쩌면 하나는 다른 것보다 빠르지 만 대부분의 경우 무시할 수 있습니다 ..

일반적으로 문자열 연결이 선호됩니다. String.format.후자에는 두 가지 주요 단점이 있습니다.

  1. 로컬 방식으로 빌드할 문자열을 인코딩하지 않습니다.
  2. 빌드 프로세스는 문자열로 인코딩됩니다.

포인트 1의 의미는 무엇인지 이해하는 것이 불가능하다는 것입니다. String.format() 호출은 단일 순차 패스에서 수행됩니다.인수의 위치를 ​​계산하는 동안 형식 문자열과 인수 사이를 왔다 갔다 해야 합니다.짧은 연결의 경우 이는 큰 문제가 되지 않습니다.그러나 이러한 경우 문자열 연결은 덜 장황합니다.

포인트 2는 건축 과정의 중요한 부분이 형식 문자열 (DSL 사용).문자열을 사용하여 코드를 표현하는 데에는 많은 단점이 있습니다.본질적으로 유형이 안전하지 않으며 구문 강조, 코드 분석, 최적화 등을 복잡하게 만듭니다.

물론 Java 언어 외부의 도구나 프레임워크를 사용할 때 새로운 요소가 작용할 수 있습니다.

특정 벤치 마크를 수행하지는 않았지만 연결이 더 빠를 수 있다고 생각합니다. string.format ()는 새 형성자를 생성하여 새로운 stringbuilder (크기가 16 숯의 크기)를 만듭니다. 특히 더 긴 문자열을 형식화하고 StringBuilder가 계속 크기를 조정 해야하는 경우 상당한 양의 오버 헤드입니다.

그러나 연결은 덜 유용하고 읽기가 더 어렵습니다. 항상 그렇듯이 코드에서 벤치 마크를 수행하여 어느 것이 더 나은지 확인할 가치가 있습니다. 리소스 번들, 로케일 등이 메모리에로드되고 코드가 Jitted 후에 서버 앱에서 차이가 무시할 수 있습니다.

아마도 모범 사례로서, 적절한 크기의 StringBuilder (부속용)로 자신의 포피터를 만들고 로케일을 사용하여 포맷 할 수있는 많은 형식이있는 경우 사용하는 것이 좋습니다.

지각 가능한 차이가있을 수 있습니다.

String.format 상당히 복잡하고 그 아래에 정규 표현을 사용하므로 어디에서나 사용하는 습관이 아니라 필요한 곳에 만 사용하는 습관이 없습니다.

StringBuilder 이곳의 누군가가 이미 지적했듯이, 몇 배 더 빠릅니다.

위의 프로그램별로 문자열 연결 및 문자열을 비교할 수 없습니다.

당신은 또한 당신의 문자열을 사용하는 위치를 상호 교환 할 수도 있습니다.

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
  }

  long end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
  start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }

  end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}

형식이 더 빠르게 작동한다는 사실에 놀랄 것입니다. 이것은 생성 된 과장된 객체가 공개되지 않을 수 있으며 메모리 할당에 문제가있을 수 있기 때문에 성능이있을 수 있습니다.

string.format에 익숙해지는 데 약간의 시간이 걸리지 만 대부분의 경우 그만한 가치가 있습니다. NRA의 세계 (아무것도 반복하지 않음)에서는 토큰 화 된 메시지 (로깅 또는 사용자)를 일정한 라이브러리에 보관하고 (정적 클래스에 금액을 선호하는지)에 필요한대로 호출하는 것이 매우 유용합니다. 현지화 여부에 관계없이. 연결 방법으로 이러한 라이브러리를 사용하려고하는 것은 연결이 필요한 모든 접근 방식으로 읽고, 문제를 해결하고, 교정하고, 관리하기가 더 어렵습니다. 교체는 옵션이지만 성능이 의심 스럽다. 수년간 사용 후 String.format의 가장 큰 문제는 다른 기능 (MSG와 같은)에 전달할 때 통화 길이가 불편하다는 것입니다. .

나는 우리와 함께 갈 수 있다고 생각합니다 MessageFormat.format 가독성과 성능 측면 모두에 능숙해야합니다.

나는 사용한 것과 동일한 프로그램을 사용했습니다 ICARO 그의 위의 답변에서 나는 사용을위한 추가 코드로 그것을 향상시켰다. MessageFormat 성능 숫자를 설명합니다.

  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = "Hi " + i + "; Hi to you " + i * 2;
    }
    long end = System.currentTimeMillis();
    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
  }

연결 = 69 밀리 초

형식 = 1435 밀리 초

MessageFormat = 200 밀리 초

업데이트 :

Sonarlint 보고서에 따라 Printf 스타일 형식 문자열을 올바르게 사용해야합니다 (Squid : S3457)

Printf 스타일 형식 문자열은 컴파일러에 의해 검증되지 않고 런타임에 해석되므로 잘못된 문자열이 생성되는 오류가 포함될 수 있습니다. 이 규칙은 java.util.formatter, java.lang.string, java.io.printstream, messageformat 및 java.io의 형식을 호출 할 때 인쇄 스타일 형식 문자열의 인수와 인수에 대한 상관 관계를 정적으로 검증합니다. .printwriter 클래스 및 java.io.printstream 또는 java.io.printwriter 클래스의 printf (...) 메소드.

나는 printf 스타일을 곱슬 껍질로 교체하고 아래와 같이 흥미로운 결과를 얻었습니다.

연결 = 69 밀리 초
형식 = 1107 밀리 초
형식 : Curly-Brackets = 416 밀리 초
MessageFormat = 215 밀리 초
MessageFormat : Curly-Brackets = 2517 밀리 초

내 결론 :
위에서 강조했듯이 String.format를 사용하여 곱슬 곱슬을 사용하는 것은 좋은 가독성과 성능의 이점을 얻는 데 좋은 선택이어야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top