Equivalenti Java di C # String.Format () e String.Join ()


Risposte:


92

L'oggetto Java String ha un formatmetodo (dalla 1.5), ma nessun joinmetodo.

Per ottenere una serie di utili metodi di utilità String non già inclusi, è possibile utilizzare org.apache.commons.lang.StringUtils .


13
collezioni google: google-collections.googlecode.com ha anche un falegname.
Ron

10
Cordiali saluti sul commento di Ron, google-collections è stato rinominato Guava qualche tempo fa.
Kevin Bourrillion

3
È necessario aggiornare questa risposta per riflettere che Java 8 introduce un String.join()metodo.
Duncan Jones

46

String.format . Per quanto riguarda l'iscrizione, devi scrivere il tuo:

 static String join(Collection<?> s, String delimiter) {
     StringBuilder builder = new StringBuilder();
     Iterator<?> iter = s.iterator();
     while (iter.hasNext()) {
         builder.append(iter.next());
         if (!iter.hasNext()) {
           break;                  
         }
         builder.append(delimiter);
     }
     return builder.toString();
 }

Quanto sopra proviene da http://snippets.dzone.com/posts/show/91


6
Più precisamente: StringBuffer per jdk1.4 e inferiori, StringBuilder per jdk1.5 e successivi, poiché quest'ultimo non è sincronizzato, quindi un po 'più veloce.
VonC

2
Prima di due invocazioni iter.hasNext () di solito aggiungo delimeter e poi "return buf.substring (0, buf.length () - delimeter.length ())".
Vilmantas Baranauskas,

2
Puoi semplificarlo un po 'uscendo presto per evitare il delimitatore: while (true) (add_iter; if (! Iter.hasNext ()) break; add_delim;}
13ren


29

A partire da Java 8, join()è ora disponibile come due metodi di classe sulla classe String. In entrambi i casi il primo argomento è il delimitatore.

Puoi passare singoli messaggi di posta CharSequenceelettronica come argomenti aggiuntivi :

String joined = String.join(", ", "Antimony", "Arsenic", "Aluminum", "Selenium");
// "Antimony, Arsenic, Alumninum, Selenium"

Oppure puoi passare unIterable<? extends CharSequence> :

List<String> strings = new LinkedList<String>();
strings.add("EX");
strings.add("TER");
strings.add("MIN");
strings.add("ATE");

String joined = String.join("-", strings);
// "EX-TER-MIN-ATE"

Java 8 aggiunge anche una nuova classe StringJoiner, che puoi usare in questo modo:

StringJoiner joiner = new StringJoiner("&");
joiner.add("x=9");
joiner.add("y=5667.7");
joiner.add("z=-33.0");

String joined = joiner.toString();
// "x=9&y=5667.7&z=-33.0"


12

È inoltre possibile utilizzare argomenti variabili per le stringhe come segue:

  String join (String delim, String ... data) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < data.length; i++) {
      sb.append(data[i]);
      if (i >= data.length-1) {break;}
      sb.append(delim);
    }
    return sb.toString();
  }

4

Per quanto riguarda l'adesione, credo che questo potrebbe sembrare un po 'meno complicato:

public String join (Collection<String> c) {
    StringBuilder sb=new StringBuilder();
    for(String s: c)
        sb.append(s);
    return sb.toString();
}

Non riesco a usare la sintassi di Java 5 quanto vorrei (che ci crediate o no, ho usato 1.0.x ultimamente) quindi potrei essere un po 'arrugginito, ma sono sicuro che il concetto sia corretto .

modifica aggiunta: le aggiunte di stringhe possono essere lente, ma se stai lavorando sul codice della GUI o su qualche routine a esecuzione breve, non importa se impieghi .005 secondi o .006, quindi se hai una raccolta chiamata "joinMe" che vuoi aggiungere a una stringa esistente "target" non sarebbe orribile semplicemente incorporare questo:

for(String s : joinMe)
    target += s;

È abbastanza inefficiente (e una cattiva abitudine), ma non qualcosa che sarai in grado di percepire a meno che non ci siano migliaia di stringhe o questo è all'interno di un ciclo enorme o il tuo codice è davvero critico per le prestazioni.

Ancora più importante, è facile da ricordare, breve, veloce e molto leggibile. Le prestazioni non sono sempre il vincitore automatico nelle scelte di design.


Il ciclo for è corretto, ma dovresti anche renderlo una Collection <String>. Se non lo fai, dovresti dire "for (Object o: c)", perché non potresti garantire che tutto in c fosse una stringa.
Michael Myers

Buon punto, sono ancora più arrugginito con i Generics. Lo modifico a.
Bill K,

Quando concatena inline: string + string + string, il compilatore utilizza effettivamente StringBuilder per aggiungere i valori. Mi chiedo se nel metodo for-loop ci sia un secondo, se il compilatore farebbe lo stesso.
Spencer Kormos

@Spencer K: Utilizza uno StringBuilder, ma ne crea uno nuovo per ogni iterazione (difficilmente il metodo più efficiente, ma allora come dovrebbe saperlo il compilatore?). Non so se il compilatore JIT potrebbe ottimizzarlo in fase di esecuzione.
Michael Myers

2
Oh-wait. Stai parlando + =? Sì, fa schifo. Il ciclo e l'appendice attualmente nella mia risposta utilizza StringBuilder ed è di questo che sto parlando. Sebbene + = sia migliorato molto, non vuoi davvero usarlo all'interno di un ciclo - non tanto che sia pieno di bug ma in quanto può funzionare come una schifezza (Bug non è un termine generalmente usato per problemi di prestazioni - almeno no nei miei 20 anni di programmazione). Anche il JIT può migliorarlo immensamente, ma non ci farei affidamento.
Bill K

4

Ecco una risposta piuttosto semplice. Usalo +=poiché contiene meno codice e lascia che l'ottimizzatore lo converta in un file StringBuilderper te. Usando questo metodo, non devi fare alcun controllo "è ultimo" nel tuo ciclo (miglioramento delle prestazioni) e non devi preoccuparti di rimuovere i delimitatori alla fine.

        Iterator<String> iter = args.iterator();
        output += iter.hasNext() ? iter.next() : "";
        while (iter.hasNext()) {
            output += "," + iter.next();
        }

1
Soluzione molto elegante, che non coinvolge le librerie di terze parti!
Denis Itskovich

2

Non volevo importare un'intera libreria Apache per aggiungere una semplice funzione di join, quindi ecco il mio trucco.

    public String join(String delim, List<String> destinations) {
        StringBuilder sb = new StringBuilder();
        int delimLength = delim.length();

        for (String s: destinations) {
            sb.append(s);
            sb.append(delim);
        }

        // we have appended the delimiter to the end 
        // in the previous for-loop. Let's now remove it.
        if (sb.length() >= delimLength) {
            return sb.substring(0, sb.length() - delimLength);
        } else {
            return sb.toString();
        }
    }

@MrSnowflake Il generatore di stringhe standard ha un metodo "removeCharAt (int index)" Non viene visualizzato nella versione in esecuzione
NSjonas

@NSjonas - sì, la sua modifica alla mia risposta è sbagliata. Il removeCharAtnon esiste, e l'intera funzione non restituisce una stringa ... risolverà questo.
Martin Konecny

già che ci sei ... questa soluzione attuale genererà una "eccezione indice fuori limite se passi in una lista vuota"
NSjonas

fatto una modifica per ottenere il taglio della lunghezza giusta se il delim è più di 1 carattere. Risolto anche un problema per cui stavi tagliando l'ultimo carattere e non il primo come
dovevi

Grazie per il feedback. Aggiornato.
Martin Konecny

1

Se desideri unire (concatenare) più stringhe in una, dovresti usare StringBuilder. È molto meglio che usare

for(String s : joinMe)
    target += s;

C'è anche un leggero vantaggio in termini di prestazioni su StringBuffer, poiché StringBuilder non utilizza la sincronizzazione.

Per un metodo di utilità generale come questo, verrà (eventualmente) chiamato molte volte in molte situazioni, quindi dovresti renderlo efficiente e non allocare molti oggetti temporanei. Abbiamo profilato molte, molte app Java diverse e quasi sempre abbiamo scoperto che la concatenazione di stringhe e le allocazioni string / char [] occupano una notevole quantità di tempo / memoria.

La nostra raccolta riutilizzabile -> il metodo stringa prima calcola la dimensione del risultato richiesto e poi crea uno StringBuilder con quella dimensione iniziale; questo evita il raddoppio / la copia non necessari del carattere interno [] utilizzato quando si aggiungono stringhe.


re: pre-calcolo delle dimensioni: quando funziona, è fantastico. Non è sempre possibile. Ricordo di aver letto da qualche parte che raddoppiare la dimensione del buffer ogni volta (o davvero moltiplicare per qualsiasi fattore fisso) dà qualcosa come n log n prestazioni, che non è poi così male. Nel caso generale, l'alternativa è fare un elenco di tutte le stringhe che intendi concatenare. Se usi un ArrayList, devi copiare di nuovo (a meno che tu non conosca in anticipo la lunghezza della sequenza) e se usi LinkedList, allora usa più ram e garbage collection con gli oggetti nodo. A volte non puoi vincere. Provaci!
Ian

Gli esempi / le indagini precedenti presupponevano una raccolta ordinata o un array di stringhe da unire. Inoltre, precalcolando la dimensione, si evitano caratteri inutilizzati aggiuntivi che di solito si traducono in una crescita dell'array char [] interno.
djb

1

Ho scritto proprio:

public static String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    Iterator<String> iter = col.iterator();
    if (iter.hasNext())
        sb.append(iter.next().toString());
    while (iter.hasNext()) {
        sb.append(delim);
        sb.append(iter.next().toString());
    }
    return sb.toString();
}

ma Collectionnon è supportato da JSP, quindi per la funzione tag ho scritto:

public static String join(List<?> list, String delim) {
    int len = list.size();
    if (len == 0)
        return "";
    StringBuilder sb = new StringBuilder(list.get(0).toString());
    for (int i = 1; i < len; i++) {
        sb.append(delim);
        sb.append(list.get(i).toString());
    }
    return sb.toString();
}

e mettere in .tldarchivio:

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee"
    <function>
        <name>join</name>
        <function-class>com.core.util.ReportUtil</function-class>
        <function-signature>java.lang.String join(java.util.List, java.lang.String)</function-signature>
    </function>
</taglib>

e usalo nei file JSP come:

<%@taglib prefix="funnyFmt" uri="tag:com.core.util,2013:funnyFmt"%>
${funnyFmt:join(books, ", ")}



0

Vedo molte implementazioni eccessivamente complesse di String.Join qui. Se non si dispone di Java 1.8 e non si desidera importare una nuova libreria, l'implementazione seguente dovrebbe essere sufficiente.

public String join(Collection<String> col, String delim) {
    StringBuilder sb = new StringBuilder();
    for ( String s : col ) {
        if ( sb.length() != 0 ) sb.append(delim);
        sb.append(s);
    }
    return sb.toString();
}

-1
ArrayList<Double> j=new ArrayList<>; 
j.add(1);
j.add(.92);
j.add(3); 
String ntop=j.toString(); //ntop= "[1, 0.92, 3]" 

Quindi, in pratica, String ntop memorizza il valore dell'intera raccolta con separatori di virgole e parentesi.


1
Non sono sicuro di quale parte della domanda risponde? Non è un String.format a meno che non si preveda di aggiungere i bit della stringa bit per bit all'array, e [1,0.92,3] non è versatile come una stringa generica .join.
Omar Kooheji

-8

Vorrei solo usare l'operatore di concatenazione di stringhe "+" per unire due stringhe. s1 += s2;


Perché è una cattiva pratica e lenta.
MrSnowflake
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.