Come faccio ad aggiungere un carattere di nuova riga per tutte le righe tranne l'ultima?

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

  •  19-08-2019
  •  | 
  •  

Domanda

Sto ripetendo una HashMap (vedi la mia domanda precedente per maggiori dettagli) e la creazione di una stringa costituita dai dati contenuti nella Mappa. Per ogni articolo, avrò una nuova riga, ma per l'ultimo elemento, non voglio la nuova riga. Come posso raggiungere questo obiettivo? Stavo pensando di poter fare una sorta di controllo per vedere se la voce è l'ultima o no, ma non sono sicuro di come farlo effettivamente.

Grazie!

È stato utile?

Soluzione

Cambia il tuo processo di pensiero da " aggiungi un'interruzione di linea tranne l'ultima volta " per "anteporre un'interruzione di linea tranne la prima volta":

boolean first = true;
StringBuilder builder = new StringBuilder();

for (Map.Entry<MyClass.Key,String> entry : data.entrySet()) {
    if (first) {
        first = false;
    } else {
        builder.append("\n"); // Or whatever break you want
    }
    builder.append(entry.key())
           .append(": ")
           .append(entry.value());
}

Altri suggerimenti

un metodo (con scuse a Jon Skeet per aver preso in prestito parte del suo codice Java):

StringBuilder result = new StringBuilder();

string newline = "";  
for (Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
    result.append(newline)
       .append(entry.key())
       .append(": ")
       .append(entry.value());

    newline = "\n";
}

Che dire di questo?

StringBuilder result = new StringBuilder();

for(Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
    builder.append(entry.key())
       .append(": ")
       .append(entry.value())
       .append("\n");
}

return builder.substring(0, builder.length()-1);

Scuse obbligatorie e grazie sia a Jon che a Joel per il "prestito" dai loro esempi.

Solitamente per questo tipo di cose uso apache-commons-lang StringUtils # join . Sebbene non sia davvero difficile scrivere tutti questi tipi di funzionalità di utilità, è sempre meglio riutilizzare le librerie comprovate esistenti. Apache-commons è pieno di cose utili come questa!

Se usi iteratore anziché per ... ogni codice potrebbe apparire così:

StringBuilder builder = new StringBuilder();

Iterator<Map.Entry<MyClass.Key, String>> it = data.entrySet().iterator();

while (it.hasNext()) {
    Map.Entry<MyClass.Key, String> entry = it.next();

    builder.append(entry.key())
    .append(": ")
    .append(entry.value());

    if (it.hasNext()) {
        builder.append("\n");
    }
}

Questo è probabilmente un esempio migliore ...

final StringBuilder buf = new StringBuilder();
final String separator = System.getProperty("line.separator"); // Platform new line
for (Map.Entry<MyClass.Key,String> entry: data.entrySet()) {
    builder.append(entry.key())
       .append(": ")
       .append(entry.value())
       .append(separator);
}
// Remove the last separator and return a string to use.
// N.b. this is likely as efficient as just using builder.toString() 
// which would also copy the buffer, except we have 1 char less 
// (i.e. '\n').
final String toUse = builder.substring(0, builder.length()-separator.length()-1);

Ecco la mia versione sintetica, che utilizza la proprietà length di StringBuilder invece di una variabile aggiuntiva:

StringBuilder builder = new StringBuilder();

for (Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
    builder.append(builder.length() > 0 ? "\n" : "")
           .append(entry.key())
           .append(": ")
           .append(entry.value());
}

(Mi scuso e grazie a Jon e Joel per "aver preso in prestito" dai loro esempi.)

Una soluzione è creare un wrapper personalizzato per StringBuilder. Non può essere esteso, quindi è necessario un wrapper.

public class CustomStringBuilder {

final String separator = System.getProperty("line.separator");

private StringBuilder builder = new StringBuilder();

public CustomStringBuilder appendLine(String str){
    builder.append(str + separator);
    return this;
}

public CustomStringBuilder append(String str){
    builder.append(str);
    return this;
}

public String toString() {
    return this.builder.toString();
}

}

Implementazione come questa:

CustomStringBuilder builder = new CustomStringBuilder();

//iterate over as needed, but a wrapper to StringBuilder with new line features.

builder.appendLine("data goes here");
return builder.toString();

Questo ha alcuni aspetti negativi:

  • Scrivere codice che in genere non è " dominio / business " centric
  • Non si utilizza una soluzione standard open source come: StringUtils.join
  • Costretto a mantenere una classe che avvolge una classe JDK che è definitiva e che quindi richiede aggiornamenti a lungo termine.

Sono andato con la soluzione StringUtils.join per iterare le raccolte e persino creare modelli di metodi di costruzione leggeri nel codice.

Supponendo che il tuo ciclo foreach passi attraverso il file per aggiungere semplicemente una nuova riga a ogni stringa e rimuovere l'ultima nuova riga quando il tuo ciclo termina.

Non sono sicuro che sia il migliore, ma è il modo più semplice per farlo:

passa in rassegna tutti i valori e aggiungi \ n normalmente nello stringbuffer. Quindi, fai qualcosa del genere

sb.setLength(sb.length()-1); 

Qui è utile un metodo di join, per integrare la divisione, perché in questo modo potresti semplicemente unire tutti gli elementi usando una nuova linea come separatore, e ovviamente non aggiunge una nuova linea alla fine del risultato; è così che lo faccio in vari linguaggi di scripting (Javascript, PHP, ecc.).

Se usi Class Separator , tu può fare

StringBuilder buf = new StringBuilder();
Separator separator = new Separator("\n");
for (Map.Entry<MyClass.Key,String> entry: data.entrySet()) {
    builder.append(separator)
       .append(entry.key())
       .append(": ")
       .append(entry.value());
}

Il separatore stampa in una stringa vuota al primo utilizzo e il separatore in tutti gli usi successivi.

Ha! Grazie a questo post ho trovato un altro modo per farlo:

public static class Extensions
{
    public static string JoinWith(this IEnumerable<string> strings, string separator)
    {
        return String.Join(separator, strings.ToArray());
    }
}

Ovviamente questo è in C # ora e Java non supporterà (ancora) il metodo di estensione, ma dovresti essere in grado di adattarlo secondo necessità & # 8212; la cosa principale è comunque l'uso di String.Join , e sono sicuro che Java abbia dell'analogo per questo.

Nota anche che questo significa eseguire un'iterazione extra delle stringhe, perché devi prima creare l'array e poi iterare su quello per costruire la tua stringa. Inoltre, creerai l'array, dove con alcuni altri metodi potresti essere in grado di cavartela con un IEnumerable che contiene solo una stringa in memoria alla volta. Ma mi piace molto la chiarezza extra.

Naturalmente, data la capacità del metodo Extension, puoi semplicemente astrarre qualsiasi altro codice in un metodo extension.

Lascia che le biblioteche facciano questo per te.

import com.sun.deploy.util.StringUtils;

e molti altri hanno StringUtils, che ha un metodo join. Puoi farlo in una riga:

StringUtils.join(list, DELIMITER);

Ma per più contesto, ecco come potresti farlo con una hashmap.

public static String getDelimitatedData(HashMap<String, String> data) {
    final String DELIMITER = "\n"; //Should make this a variable
    ArrayList<String> entries = new ArrayList<>();

    for (Map.Entry<String, String> entry : data.entrySet()) {
        entries.add(entry.getKey() + ": " + entry.getValue());
    }

    return StringUtils.join(entries, DELIMITER);
}

Utilizza la stringa JDK che unisce le API:

String result = data.stream()
    .map((k,v) -> String.format("%s : %s", k, v)
    .collect(Collectors.joining("\n"));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top