Verwenden von Java 8 zum Konvertieren einer Liste von Objekten in eine Zeichenfolge, die mit der toString () -Methode abgerufen wurde

206

In Java 8 gibt es viele nützliche neue Dinge. Beispielsweise kann ich mit einem Stream über eine Liste von Objekten iterieren und dann die Werte aus einem bestimmten Feld der ObjectInstanzen der Instanzen summieren . Z.B

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

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

Daher frage ich, ob es eine Möglichkeit gibt, eine zu erstellen String, die die Ausgabe der toString()Methode aus den Instanzen in einer einzelnen Zeile verkettet .

List<Integer> list = ...

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

Nehmen wir an , listganze Zahlen enthält 1, 2und 3erwarte ich , dass concatenatedist "123"oder "1,2,3".

mat_boy
quelle

Antworten:

368

Eine einfache Möglichkeit besteht darin, Ihre Listenelemente in a anzuhängen 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);

Sie können auch versuchen:

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

Erläuterung: map konvertiert den Integer-Stream in einen String-Stream und reduziert ihn dann als Verkettung aller Elemente.

Hinweis: Dies ist, normal reductionwas in O (n 2 ) ausgeführt wird.

Verwenden Sie für eine bessere Leistung eine StringBuilderoder eine mutable reductionähnliche Antwort von F. Böller.

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

Ref: Stream-Reduzierung

Shail016
quelle
2
Ja, keine Inline-Lösung, sondern eine Lösung.
mat_boy
habe das nicht verstanden. Was erwarten Sie?
Shail016
2
Ich verstehe nicht, warum "normale Reduktion, die in O (n2) durchgeführt wird". für mich "sieht" es eher aus wie O (n) ...
datahaki
2
@datahaki Ich bin kein Java-Typ, aber ich würde vermuten, dass die iterative Verkettung (Reduktion) von Zeichenfolgen bei jeder Iteration eine Array- (Neu-) Zuweisung erfordert, was sie zu O (n ^ 2) macht. joinAuf der anderen Seite würde ein einzelnes Array vorab zugewiesen, das groß genug ist, um die endgültige Zeichenfolge vor dem Auffüllen zu speichern, sodass O (n) wird.
Eli Korvigo
2
Sehr guter Tipp. Vielleicht können Sie mit der Notation "::" vereinfachen. Also , list.stream().map(Object::toString).reduce("", String::concat). Die Verwendung map e-> e.toString()ist etwas redundant.
e2a
194

joiningIn der API befindet sich ein Kollektor . Es ist eine statische Methode in Collectors.

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

Nicht perfekt wegen des notwendigen Anrufs toString, funktioniert aber. Unterschiedliche Trennzeichen sind möglich.

F. Böller
quelle
7
Der Vollständigkeit halber: Damit genau dieser Code funktioniert, müssen Sieimport static java.util.stream.Collectors.joining;
Mahdi
7
Warum brauchen wir das Mapping und das explizite "toString"? Wenn der Kollektor String-Elemente erwartet, sollte die Konvertierung implizit aufgerufen werden, nein?!
Basel Shishani
10

Nur für den Fall, dass jemand versucht, dies ohne Java 8 zu tun, gibt es einen ziemlich guten Trick. List.toString () gibt bereits eine Sammlung zurück, die folgendermaßen aussieht:

[1,2,3]

Abhängig von Ihren spezifischen Anforderungen kann dies nach Belieben nachbearbeitet werden, solange Ihre Listenelemente nicht [] oder ,.

Zum Beispiel:

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

oder wenn Ihre Daten eckige Klammern enthalten könnten:

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

erhalten Sie eine ziemlich vernünftige Ausgabe.

Ein Array-Element in jeder Zeile kann folgendermaßen erstellt werden:

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

Ich habe diese Technik verwendet, um HTML-Tooltips aus einer Liste in einer kleinen App zu erstellen, mit etwa:

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

Wenn Sie ein Array haben, beginnen Sie stattdessen mit Arrays.asList (Liste) .toString ()

Ich werde die Tatsache völlig besitzen, dass dies nicht optimal ist, aber es ist nicht so ineffizient, wie Sie vielleicht denken, und es ist ziemlich einfach zu lesen und zu verstehen. Es ist jedoch ziemlich unflexibel - versuchen Sie insbesondere nicht, die Elemente mit replaceAll zu trennen, wenn Ihre Daten Kommas enthalten könnten, und verwenden Sie die Teilzeichenfolgenversion, wenn Ihre Daten eckige Klammern enthalten, aber für ein Array von Zahlen ist dies ziemlich viel perfekt.

Bill K.
quelle
Während dies normalerweise funktioniert, ist es nicht garantiert - Listerzwingt nicht die Struktur von toString(). AbstractCollectionVerwendet diese Struktur jedoch standardmäßig, und ich denke, dass alle allgemeinen ListImplementierungen in Java SE dies auch tun. Als Beispiel für eine, die dies nicht tut, wird sie org.springframework.util.AutoPopulatingListim Frühjahr nicht implementiert toString()und würde daher z org.springframework.util.AutoPopulatingList@170a1. B. " " zurückgeben.
M. Justin
Arrays.toString()Dies wäre eine bessere Wahl Arrays.asList(list).toString(), da es definiert ist, eine äquivalente Zeichenfolge zurückzugeben, prägnanter ist und keine zusätzliche Objekterstellung erfordert.
M. Justin
@ M.Justin Kein schlechter Punkt für den Array-Teil dieser Antwort, aber was ist mit einer einfachen "Liste"? Sie können Arrays.toString () dafür nicht wirklich verwenden. Wie würden Sie vorschlagen, diese Technik für etwas wie die von Ihnen erwähnte AutoPopulatingList zu verwenden? Vielleicht: neue ArrayList (autoPopulatingList) .toString ()? Oder Sie könnten es in ein Array konvertieren. Wenn Sie auf ein solches Problem gestoßen sind, sollten Sie vielleicht hoffen, dass Sie auf 1.8 oder höher sind und eine der anderen Antworten in diesem Thread verwenden könnten ...
Bill K
5

Die anderen Antworten sind in Ordnung. Sie können jedoch auch Collectors.toList () als Parameter an Stream.collect () übergeben , um die Elemente als ArrayList zurückzugeben.

System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );
Eddie B.
quelle
Wenn Sie System.out.print(ln) verwenden (list), wird die toString()Methode der Elemente zum Drucken der Liste verwendet. Dieser Code wiederholt also nur, was in der toStringListe passiert .
Shirkam
1
Sollte Collectors.toList () sein, es sei denn, Sie importieren statische Daten.
Mwieczorek
Ja ... ich habe statisch für das Beispiel importiert. Ich sagte "Collectors.toList ()" im Hauptteil der Antwort ...;)
Eddie B
2

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

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

Oder

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

Mit diesem Ansatz können Sie auch ein Zeichenfolgenergebnis aus einer Liste von Objekten erstellen. Beispiel

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

Hier können Sie mit der Reduzierungsfunktion einen Anfangswert festlegen, an den Sie eine neue Zeichenfolge anhängen möchten. Beispiel:

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

Das Testen beider in Shail016 und bpedroso answer ( https://stackoverflow.com/a/24883180/2832140 ) vorgeschlagenen Ansätze , das einfache StringBuilder+ append(String)innerhalb einer forSchleife, scheint viel schneller als auszuführen list.stream().map([...].

Beispiel: Dieser Code durchläuft einen Map<Long, List<Long>>Build-JSON-String mit 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());
}

Auf meiner Entwicklungs-VM benötigt junit normalerweise zwischen 0,35 und 1,2 Sekunden, um den Test auszuführen. Bei Verwendung dieses folgenden Codes dauert es zwischen 0,15 und 0,33 Sekunden:

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());
}
Lorenzo Cipriani
quelle
1

In der String-API gibt es eine Methode für die Anwendungsfälle "Joining List of String". Sie benötigen nicht einmal Stream.

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

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

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

Können wir das versuchen?

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

    }
Kumar Abhishek
quelle
0
String actual = list.stream().reduce((t, u) -> t + "," + u).get();
Joe Almore
quelle
7
Normalerweise ist es eine gute Idee, Ihrem Beitrag eine Erklärung hinzuzufügen, die die Funktionsweise des Codes beschreibt. Dadurch können neue Entwickler verstehen, was der Code tut.
Caleb Kleveter
1
@CalebKleveter reduziert a Listauf ein einzelnes Stringund trennt jedes Element durch ein Komma ( ,).
Joe Almore
2
Ihre Lösung funktioniert NUR, wenn die Liste eine Liste <String> ist. OP hat eine solche Klasse nicht angegeben. In seiner Frage wird sogar eine Liste <Integer> angezeigt. Ihr Code wird in diesem Fall nicht kompiliert.
RubioRic
0

Ich werde die Streams-API verwenden, um einen Stream von Ganzzahlen in eine einzelne Zeichenfolge zu konvertieren. Das Problem bei einigen der bereitgestellten Antworten besteht darin, dass sie aufgrund der String-Erstellung eine O (n ^ 2) -Laufzeit erzeugen. Eine bessere Lösung besteht darin, einen StringBuilder zu verwenden und die Strings dann als letzten Schritt zusammenzufügen.

//              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();

Sie können diesen Prozess noch einen Schritt weiter gehen, indem Sie die Objektsammlung in eine einzelne Zeichenfolge konvertieren.

// 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();
Digital-Logik
quelle
-1

Mit Java 8+

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

Nicht die effizienteste, aber es ist eine Lösung mit einer kleinen Menge Code.

James Wierzba
quelle
-2

Das können Sie auch so machen.

    List<String> list = Arrays.asList("One", "Two", "Three");
    String result = String.join(", ", list);
    System.out.println(result);
Evgenii
quelle
Ihre Lösung funktioniert NUR, wenn die Liste eine Liste <String> ist. OP hat eine solche Klasse nicht angegeben. In seiner Frage wird sogar eine Liste <Integer> angezeigt.
RubioRic