Utilizzo di Java 8 per convertire un elenco di oggetti in una stringa ottenuta dal metodo toString ()


206

Ci sono molte nuove cose utili in Java 8. Ad esempio, posso iterare con un flusso su un elenco di oggetti e quindi sommare i valori da un campo specifico delle Objectistanze di. Per esempio

public class AClass {
  private int value;
  public int getValue() { return value; }
}

Integer sum = list.stream().mapToInt(AClass::getValue).sum();

Pertanto, sto chiedendo se esiste un modo per creare un oggetto Stringche concatena l'output del toString()metodo dalle istanze in un'unica riga.

List<Integer> list = ...

String concatenated = list.stream().... //concatenate here with toString() method from java.lang.Integer class

Si supponga che listcontiene numeri interi 1, 2e 3, mi aspetto che concatenatedsia "123"o "1,2,3".


Risposte:


369

Un modo semplice è quello di aggiungere le voci dell'elenco in a StringBuilder

   List<Integer> list = new ArrayList<>();
   list.add(1);
   list.add(2);
   list.add(3);

   StringBuilder b = new StringBuilder();
   list.forEach(b::append);

   System.out.println(b);

puoi anche provare:

String s = list.stream().map(e -> e.toString()).reduce("", String::concat);

Spiegazione: map converte il flusso intero in stream String, quindi viene ridotto come concatenazione di tutti gli elementi.

Nota: questo è ciò normal reductionche si esibisce in O (n 2 )

per prestazioni migliori usa una StringBuildero mutable reductionsimile alla risposta di F. Böller.

String s = list.stream().map(Object::toString).collect(Collectors.joining(","));

Rif: riduzione del flusso


2
Sì, non una soluzione in linea, ma una soluzione.
mat_boy,

non l'ho capito. Che cosa ti aspetti
Shail016,

2
non capisco perché "riduzione normale che si comporta in O (n2)". per me "sembra" più simile a O (n) ...
datahaki

2
@datahaki Non sono un tipo Java, ma immagino che la concatenazione (riduzione) di stringhe iterative richieda una (ri) allocazione di array ad ogni iterazione, il che la rende O (n ^ 2). joindall'altro lato preallocerebbe un singolo array abbastanza grande da memorizzare l'ultima stringa prima di popolarla, rendendola O (n).
Eli Korvigo,

2
Consiglio molto buono. Forse puoi semplificare usando la notazione "::". Quindi list.stream().map(Object::toString).reduce("", String::concat). L'uso map e-> e.toString()è un po 'ridondante.
e2a,

194

C'è un raccoglitore joiningnell'API. È un metodo statico in Collectors.

list.stream().map(Object::toString).collect(Collectors.joining(","))

Non perfetto a causa della chiamata necessaria di toString, ma funziona. Sono possibili diversi delimitatori.


7
Per completezza: per far funzionare quel codice esatto, è necessarioimport static java.util.stream.Collectors.joining;
Mahdi,

7
Perché abbiamo bisogno della mappatura e del "toString" esplicito? Se il collector si aspetta elementi String, allora la conversione dovrebbe essere invocata implicitamente, no ?!
Basilea Shishani,

10

Nel caso in cui qualcuno stia cercando di farlo senza java 8, c'è un bel trucco. List.toString () restituisce già una raccolta simile a questa:

[1,2,3]

A seconda dei requisiti specifici, questo può essere postelaborato in base a ciò che si desidera purché le voci dell'elenco non contengano [] o,.

Per esempio:

list.toString().replace("[","").replace("]","") 

o se i tuoi dati potrebbero contenere parentesi quadre questo:

String s=list.toString();
s = s.substring(1,s.length()-1) 

ti darà un risultato abbastanza ragionevole.

È possibile creare un elemento dell'array su ciascuna riga in questo modo:

list.toString().replace("[","").replace("]","").replaceAll(",","\r\n")

Ho usato questa tecnica per creare tooltip html da un elenco in una piccola app, con qualcosa del tipo:

list.toString().replace("[","<html>").replace("]","</html>").replaceAll(",","<br>")

Se disponi di un array, inizia invece con Arrays.asList (list) .toString ()

Possederò totalmente il fatto che ciò non è ottimale, ma non è così inefficiente come si potrebbe pensare ed è piuttosto semplice da leggere e comprendere. È, tuttavia, abbastanza inflessibile - in particolare non provare a separare gli elementi con sostituisci Tutti se i tuoi dati potrebbero contenere virgole e utilizzare la versione di sottostringa se hai parentesi quadre nei tuoi dati, ma per una matrice di numeri è praticamente Perfetto.


Mentre questo di solito funziona, non è garantito - Listnon impone la struttura di toString(). AbstractCollectiontuttavia, utilizza questa struttura per impostazione predefinita e penso che lo Listfacciano anche tutte le implementazioni di uso generale in Java SE. Come esempio di uno che non lo fa, org.springframework.util.AutoPopulatingListin primavera non si implementa toString()e quindi ritornerebbe ad es. " org.springframework.util.AutoPopulatingList@170a1".
M. Justin,

Arrays.toString()sarebbe una scelta migliore rispetto a Arrays.asList(list).toString(), poiché è definita per restituire una stringa equivalente, è più concisa e non richiede la creazione di oggetti aggiuntivi.
M. Justin,

@ M.Justin Non è un brutto punto per la parte dell'array di questa risposta, ma che dire di un semplice "Elenco"? Non puoi davvero usare Arrays.toString () su questo. Come consiglieresti di usare questa tecnica su qualcosa come AutoPopulatingList che hai citato? Forse: new ArrayList (autoPopulatingList) .toString ()? O immagino che potresti convertirlo in un array. Forse se ti sei imbattuto in un problema del genere, dovresti sperare di essere in 1.8 o versioni successive e di poter utilizzare una delle altre risposte in questa discussione ...
Bill K,

5

Le altre risposte vanno bene. Tuttavia, è anche possibile passare Collectors.toList () come parametro a Stream.collect () per restituire gli elementi come ArrayList.

System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );

Se usi System.out.print(ln) (list)userà il toString()metodo degli elementi per stampare l'elenco. Quindi, questo pezzo di codice sta semplicemente ripetendo ciò che accade all'interno toStringdell'elenco.
Shirkam,

1
Dovrebbe essere Collectors.toList () a meno che non si stia importando statico.
mwieczorek,

Sì ... Ho importato statico per l'esempio. Ho dichiarato, "Collectors.toList ()" nel corpo della risposta ...;)
Eddie B,

2

StringListName = ObjectListName.stream (). Map (m -> m.toString ()) .collect (Collectors.toList ());


2

Esiste un metodo nell'API String per quei casi di utilizzo "elenco di stringhe", non è nemmeno necessario Stream.

List<String> myStringIterable = Arrays.asList("baguette", "bonjour");

String myReducedString = String.join(",", myStringIterable);

// And here you obtain "baguette,bonjour" in your myReducedString variable

1
List<String> list = Arrays.asList("One", "Two", "Three");
    list.stream()
            .reduce("", org.apache.commons.lang3.StringUtils::join);

O

List<String> list = Arrays.asList("One", "Two", "Three");
        list.stream()
                .reduce("", (s1,s2)->s1+s2);

Questo approccio consente anche di creare un risultato stringa da un elenco di oggetti Esempio

List<Wrapper> list = Arrays.asList(w1, w2, w2);
        list.stream()
                .map(w->w.getStringValue)
                .reduce("", org.apache.commons.lang3.StringUtils::join);

Qui la funzione di riduzione consente di avere un valore iniziale al quale si desidera aggiungere una nuova stringa Esempio:

 List<String> errors = Arrays.asList("er1", "er2", "er3");
            list.stream()
                    .reduce("Found next errors:", (s1,s2)->s1+s2);

1

Testando entrambi gli approcci suggeriti in Shail016 e bpedroso answer ( https://stackoverflow.com/a/24883180/2832140 ), il semplice StringBuilder+ append(String)all'interno di un forciclo sembra eseguire molto più velocemente dilist.stream().map([...] .

Esempio: questo codice guida attraverso una Map<Long, List<Long>>build una stringa json, usando list.stream().map([...]:

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");
        sb.append(entry.getValue().stream().map(Object::toString).collect(Collectors.joining(",")));
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}

Sulla mia macchina virtuale dev, junit richiede solitamente tra 0,35 e 1,2 secondi per eseguire il test. Mentre, utilizzando questo codice seguente, sono necessari tra 0,15 e 0,33 secondi:

if (mapSize > 0) {
    StringBuilder sb = new StringBuilder("[");

    for (Map.Entry<Long, List<Long>> entry : threadsMap.entrySet()) {

        sb.append("{\"" + entry.getKey().toString() + "\":[");

        for (Long tid : entry.getValue()) {
            sb.append(tid.toString() + ", ");
        }
        sb.delete(sb.length()-2, sb.length());
        sb.append("]}, ");
    }
    sb.delete(sb.length()-2, sb.length());
    sb.append("]");
    System.out.println(sb.toString());
}

0

Possiamo provare questo.

public static void main(String []args){
        List<String> stringList = new ArrayList<>();
        for(int i=0;i< 10;i++){
            stringList.add(""+i);
        }
        String stringConcated = String.join(",", stringList);
        System.out.println(stringConcated);

    }

0
String actual = list.stream().reduce((t, u) -> t + "," + u).get();

7
Di solito è una buona idea aggiungere una spiegazione al tuo post che parla di come funziona il codice. Ciò consente ai nuovi sviluppatori di capire cosa fa il codice.
Caleb Kleveter,

1
@CalebKleveter riduce a Lista singolo Stringe separa ogni elemento con una virgola ( ,).
Joe Almore,

2
La soluzione funziona SOLO se l'elenco è un Elenco <String>. OP non ha specificato tale classe. Nella sua domanda viene anche indicato un Elenco <Integer>. Il tuo codice non viene compilato in quel caso.
RubioRic

0

Ho intenzione di utilizzare i flussi API per convertire un flusso di numeri interi in una singola stringa. Il problema con alcune delle risposte fornite è che producono un runtime O (n ^ 2) a causa della creazione di stringhe. Una soluzione migliore consiste nell'utilizzare StringBuilder e quindi unire le stringhe come passaggio finale.

//              Create a stream of integers 
    String result = Arrays.stream(new int[]{1,2,3,4,5,6 })                
            // collect into a single StringBuilder
            .collect(StringBuilder::new, // supplier function
                    // accumulator - converts cur integer into a string and appends it to the string builder
                    (builder, cur) -> builder.append(Integer.toString(cur)),
                    // combiner - combines two string builders if running in parallel
                    StringBuilder::append) 
            // convert StringBuilder into a single string
            .toString();

Puoi fare un ulteriore passo in avanti con questo processo convertendo la raccolta di oggetti in una singola stringa.

// Start with a class definition
public static class AClass {
    private int value;
    public int getValue() { return value; }
    public AClass(int value) { this.value = value; }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

// Create a stream of AClass objects
        String resultTwo = Arrays.stream(new AClass[]{
                new AClass(1),
                new AClass(2),
                new AClass(3),
                new AClass(4)
        })
                // transform stream of objects into a single string
                .collect(StringBuilder::new,
                        (builder, curObj) -> builder.append(curObj.toString()),
                        StringBuilder::append
                )
            // finally transform string builder into a single string
            .toString();

-1

Con Java 8+

String s = Arrays.toString(list.stream().toArray(AClass[]::new));

Non è il più efficiente, ma è una soluzione con una piccola quantità di codice.


-2

Inoltre, puoi fare così.

    List<String> list = Arrays.asList("One", "Two", "Three");
    String result = String.join(", ", list);
    System.out.println(result);

La soluzione funziona SOLO se l'elenco è un Elenco <String>. OP non ha specificato tale classe. Nella sua domanda viene anche indicato un Elenco <Integer>.
RubioRic
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.