Domanda

C'è una differenza percettibile tra utilizzando String.format e concatenazione di stringhe in Java?

Io tendo ad usare String.format ma di tanto in tanto sarà scivolare e usare una concatenazione. Mi chiedevo se uno era migliore rispetto agli altri.

Il mio modo di vedere, String.format ti dà più potere nella "formattazione" la stringa; e la concatenazione significa che non devi preoccuparti di accidentalmente mettere in% s in più o manca uno fuori.

String.format è anche più breve.

Qual è il più leggibile dipende da come funziona la testa.

È stato utile?

Soluzione

Io suggerirei che è prassi meglio usare String.format(). La ragione principale è che String.format() può essere più facilmente localizzato con testo caricato da file di risorse, mentre la concatenazione non può essere localizzata senza produrre un nuovo file eseguibile con il codice diverso per ogni lingua.

Se si pensa di essere la vostra applicazione localizzabile si dovrebbe anche prendere l'abitudine di specificare posizioni di argomento per il formato di gettoni così:

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

Questo può quindi essere localizzato e avere il nome e il tempo gettoni scambiato senza richiedere una ricompilazione del file eseguibile per tenere conto del diverso ordinamento. Con posizioni di argomento è anche possibile riutilizzare lo stesso argomento senza passare in funzione due volte:

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

Altri suggerimenti

A proposito di prestazioni:

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");
}

I risultati di temporizzazione sono i seguenti:

  • La concatenazione = 265 millisecondi
  • Formato = 4141 millisecondi

Quindi, la concatenazione è molto più veloce di String.Format.

Poiché non v'è discussione su prestazioni ho pensato che vorrei aggiungere in un confronto che ha incluso StringBuilder. E 'infatti più veloce del concat e, naturalmente, l'opzione String.Format.

Per rendere questo una sorta di mele a confronto mele ho un'istanza di un nuovo StringBuilder nel ciclo piuttosto che all'esterno (questo è in realtà più veloce di fare solo un'esemplificazione molto probabilmente a causa del sovraccarico di spazio ri-allocazione per il looping accodare a la fine di un costruttore).

    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 INFO [TestMain] - Formato = 1.416 millisecondi
  • 2012-01-11 16: 30: 46.190 INFO [TestMain] - concatenazione = 134 millisecondi
  • 2012-01-11 16: 30: 46.313 INFO [TestMain] - String Builder = 117 millisecondi

Un problema con .format è che si perde la sicurezza di tipo statico. Si può avere troppo pochi argomenti per il formato, e si può avere il tipo sbagliato per gli identificatori di formato -. Sia che porti ad un IllegalFormatException in fase di esecuzione , per cui si potrebbe finire con il codice di registrazione che rompe la produzione

Al contrario, gli argomenti + possono essere testati dal compilatore.

  

Qual è il più leggibile dipende da come funziona la testa.

Hai la tua risposta proprio lì.

E 'una questione di gusto personale.

La concatenazione di stringhe è marginalmente più veloce, suppongo, ma che dovrebbe essere trascurabile.

Ecco un test con più dimensioni del campione in millisecondi.

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

Ecco lo stesso test come sopra, con la modifica di chiamare la toString () metodo sul StringBuilder . I risultati sotto riportati evidenziano che l'approccio StringBuilder è solo un po 'più lento di concatenazione di stringhe utilizzando il + operatore.

file: 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");

  }
}

comandi della shell: (compilare ed eseguire StringTest 5 volte)

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

Risultati:

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() è più di stringhe solo concatenare. Ad esempio, è possibile visualizzare i numeri in un locale specifico utilizzando String.format().

Tuttavia, se non si cura di localizzazione, non v'è alcuna differenza funzionale. Forse quello è più veloce rispetto agli altri, ma nella maggior parte dei casi sarà trascurabile ..

In generale, concatenazione di stringhe deve essere preferiva over String.format. Quest'ultimo ha due svantaggi principali:

  1. Non codificare la stringa da essere costruito in modo locale.
  2. Il processo di costruzione è codificato in una stringa.

Per il punto 1, voglio dire che non è possibile capire cosa una chiamata String.format() sta facendo in un unico passaggio sequenziale. Si è costretti ad andare avanti e indietro tra la stringa di formato e gli argomenti, mentre contando la posizione degli argomenti. Per brevi concatenazioni, questo non è un gran problema. In questi casi, tuttavia, la concatenazione di stringhe è meno prolisso.

punto 2, intendo che la parte importante del processo di costruzione è codificato nel stringa di formato (usando un DSL). Utilizzando le stringhe per rappresentare il codice ha molti svantaggi. Non è intrinsecamente type-safe, e complica l'evidenziazione della sintassi, analisi del codice, ottimizzazione, ecc.

Naturalmente, quando si utilizzano strumenti o strutture esterne al linguaggio Java, nuovi fattori possono entrare in gioco.

Non ho fatto alcun benchmark specifici, ma penserei che la concatenazione può essere più veloce. String.Format () crea una nuova Formatter, che, a sua volta, crea un nuovo StringBuilder (con una dimensione di soli 16 caratteri). Questa è una discreta quantità di overhead, soprattutto se si sta formattando una stringa più lunga e StringBuilder continua ad avere per ridimensionare.

Tuttavia, la concatenazione è meno utile e più difficile da leggere. Come sempre, vale la pena fare un punto di riferimento sul codice per vedere che è meglio. Le differenze possono essere trascurabili in application server dopo che il bundle di risorse, locali, ecc vengono caricati in memoria e il codice è JITted.

Forse come best practice, sarebbe una buona idea per creare il proprio Formatter con un correttamente dimensionato StringBuilder (come aggiungibile) e Locale e l'uso che se avete un sacco di formattazione da fare.

Ci potrebbe essere una differenza percepibile.

String.format è abbastanza complessa e utilizza un'espressione regolare sotto, in modo da non prendere l'abitudine di usarlo ovunque, ma solo quando ne avete bisogno.

StringBuilder sarebbe un ordine di grandezza più veloce (come qualcuno qui già sottolineato).

Non si può paragonare concatenazione di stringhe e String.Format dal programma di cui sopra.

Si può provare questo anche scambiando la posizione di utilizzare lo String.Format e concatenazione nel blocco di codice come il seguente

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") ;
}

Sarete sorpresi di vedere che Format lavora più velocemente qui. Questo perché gli oggetti creati intial non potrebbero essere rilasciati e non ci può essere un problema con allocazione di memoria e quindi le prestazioni.

Ci vuole un po 'di tempo per abituarsi String.Format, ma ne vale la pena nella maggior parte dei casi. Nel mondo della NRA (mai ripetere nulla) è estremamente utile per mantenere i vostri messaggi tokenized (registrazione o utente) in una libreria Constant (io preferisco ciò che equivale a una classe statica) e li chiamo, se necessario, con String.Format indipendentemente dal fatto che sono la localizzazione o meno. Cercando di utilizzare tale libreria con un metodo di concatenazione è più difficile da leggere, risolvere i problemi, correggere le bozze, e gestire con qualsiasi qualsiasi approccio che richiede la concatenazione. La sostituzione è un'opzione, ma dubito che sia performante. Dopo anni di utilizzo, il mio più grande problema con String.Format è la lunghezza della chiamata è sconvenientemente lungo quando sto passando in un'altra funzione (come Msg), ma che è facile andare in giro con una funzione personalizzata per servire come un alias .

Credo che possiamo andare con MessageFormat.format come dovrebbe essere bravo a entrambi gli aspetti delle prestazioni di leggibilità e anche.

Ho usato lo stesso programma, che quella usata da Icaro nella sua risposta di cui sopra e ho migliorato con aggiungendo il codice per l'utilizzo di MessageFormat per spiegare i numeri delle prestazioni.

  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");
  }
  

concatenazione = 69 millisecondi

     

Formato = 1.435 millisecondi

     

MessageFormat = 200 millisecondi

Aggiornamenti:

Come per SonarLint Rapporto, stringhe di formato in stile Printf devono essere utilizzati in modo corretto (calamari: S3457)

A causa stringhe di formato in stile printf sono interpretati in fase di esecuzione, piuttosto che convalidati dal compilatore, possono contenere degli errori che provocano le corde sbagliate in fase di creazione. Questa regola convalida staticamente la correlazione delle stringhe di formato printf-style per i loro argomenti, quando chiamano il formato (...) i metodi di java.util.Formatter, java.lang.String, java.io.PrintStream, MessageFormat, e java.io classi .PrintWriter e printf (...) i metodi di classi o java.io.PrintStream java.io.PrintWriter.

sostituisce la stile printf con le graffe-staffe e ho qualcosa risultati interessanti, come di seguito.

  

La concatenazione = 69 millisecondi
Format = 1.107 millisecondi
   Formato: Riccio-parentesi = 416 millisecondi
MessageFormat = 215   millisecondo
MessageFormat: Riccio-parentesi = 2.517 millisecondi

La mia conclusione:
Come ho sottolineato in precedenza, utilizzando String.Format con graffe-staffe dovrebbe essere una buona scelta per ottenere benefici di una buona leggibilità e anche le prestazioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top