274

There are a lot of useful new things in Java 8. E.g., I can iterate with a stream over a list of objects and then sum the values from a specific field of the Object's instances. E.g.

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

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

Thus, I'm asking if there is any way to build a String that concatenates the output of the toString() method from the instances in a single line.

List<Integer> list = ...

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

Suppose that list contains integers 1, 2 and 3, I expect that concatenated is "123" or "1,2,3".

Lii
  • 11,553
  • 8
  • 64
  • 88
mat_boy
  • 12,998
  • 22
  • 72
  • 116
  • Possible duplicate of [Java: convert List to a String](https://stackoverflow.com/questions/1751844/java-convert-liststring-to-a-string) – Vadzim Nov 20 '17 at 19:25

15 Answers15

492

One simple way is to append your list items 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);

you can also try:

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

Explanation: map converts Integer stream to String stream, then its reduced as concatenation of all the elements.

Note: This is normal reduction which performs in O(n2)

for better performance use a StringBuilder or mutable reduction similar to F. Böller's answer.

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

Ref: Stream Reduction

Community
  • 1
  • 1
Shailesh Aswal
  • 6,632
  • 1
  • 20
  • 27
  • 2
    Yeah, not an inline solution, but a solution. – mat_boy Jul 22 '14 at 09:18
  • didn't get that. What are you expecting? – Shailesh Aswal Jul 22 '14 at 09:20
  • Sure it's inline :: System.out.println( list.stream().map( e -> e.toString() ).reduce( "", String::concat ) ); – Edward J Beckett Dec 18 '14 at 08:32
  • 2
    i don't understand why "normal reduction which performs in O(n2)". to me it "looks" more like O(n)... – datahaki Mar 15 '17 at 12:55
  • 3
    @datahaki I'm not a Java guy, but I'd guess that iterative string concatenation (reduction) requires array (re)allocation at each iteration, which makes it O(n^2). `join` on the other side would preallocate a single array large enough to store the final string before populating it, making it O(n). – Eli Korvigo Jan 18 '18 at 14:44
  • 2
    Very good tip. Maybe you can simplify using "::" notation. So, `list.stream().map(Object::toString).reduce("", String::concat)`. Using `map e-> e.toString()` is a little bit redundant. – e2a Feb 09 '18 at 13:02
242

There is a collector joining in the API. It's a static method in Collectors.

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

Not perfect because of the necessary call of toString, but works. Different delimiters are possible.

Daniel Perník
  • 5,464
  • 2
  • 38
  • 46
F. Böller
  • 4,194
  • 2
  • 20
  • 32
  • 8
    For completeness: to get that exact code to work, you need to `import static java.util.stream.Collectors.joining;` – Mahdi Feb 24 '16 at 10:36
  • 8
    Why do we need the mapping and the explicit `toString'? If the collector is expecting String elements then the conversion should be invoked implicitly, no?! – Basel Shishani Sep 05 '16 at 03:41
  • 1
    @BaselShishani No. The collector expects `CharSequence` objects and therefore a conversion is required. You would need a collector that accepts objects and does `toString()` itself in order to skip the `map(Object::toString)`. – Datz Mar 14 '23 at 14:27
11

Just in case anyone is trying to do this without java 8, there is a pretty good trick. List.toString() already returns a collection that looks like this:

[1,2,3]

Depending on your specific requirements, this can be post-processed to whatever you want as long as your list items don't contain [] or , .

For instance:

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

or if your data might contain square brackets this:

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

will get you a pretty reasonable output.

One array item on each line can be created like this:

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

I used this technique to make html tooltips from a list in a small app, with something like:

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

If you have an array then start with Arrays.asList(list).toString() instead

I'll totally own the fact that this is not optimal, but it's not as inefficient as you might think and is pretty straightforward to read and understand. It is, however, quite inflexible--in particular don't try to separate the elements with replaceAll if your data might contain commas and use the substring version if you have square brackets in your data, but for an array of numbers it's pretty much perfect.

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • While this will usually work, it's not guaranteed to — `List` does not enforce the structure of `toString()`. `AbstractCollection` however, does use this structure by default, and I think all the general purpose `List` implementations in Java SE do as well. As an example of one that doesn't, `org.springframework.util.AutoPopulatingList` in Spring doesn't implement `toString()` and thus would return e.g. "`org.springframework.util.AutoPopulatingList@170a1`". – M. Justin Apr 17 '20 at 20:12
  • `Arrays.toString()` would be a better choice than `Arrays.asList(list).toString()`, as it is defined to return an equivalent string, it's more concise, and it doesn't require the additional object creation. – M. Justin Apr 17 '20 at 20:15
  • @M.Justin Not a bad point for the array portion of this answer, but what about a plain "List"? You can't really use Arrays.toString() on that. How would you suggest using this technique on something like the AutoPopulatingList you mentioned? Maybe: new ArrayList(autoPopulatingList).toString()? Or I guess you could convert it into an array. Maybe if you ran into such a problem you should hope you are on 1.8 or later and could use one of the other answers in this thread... – Bill K Apr 20 '20 at 22:13
10

There is a method in the String API for those "joining list of string" usecases, you don't even need Stream.

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

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

// And here you obtain "baguette,bonjour" in your myReducedString variable
bachrc
  • 1,106
  • 1
  • 12
  • 20
6

The other answers are fine. However, you can also pass Collectors.toList() as parameter to Stream.collect() to return the elements as an ArrayList.

System.out.println( list.stream().map( e -> e.toString() ).collect( toList() ) );
Edward J Beckett
  • 5,061
  • 1
  • 41
  • 41
  • If you use `System.out.print`(ln)`(list)` it will use the `toString()` method of the elements to print the list. So, this piece of code is just repeating what happens inside the `toString` of List. – Shirkam Oct 24 '17 at 13:26
  • 1
    Should be Collectors.toList() unless you're importing static. – mwieczorek Apr 05 '18 at 15:56
  • Yep... I imported static for the example. I stated, "Collectors.toList()" in the body of the answer... ;) – Edward J Beckett Apr 05 '18 at 16:38
4

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

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

Or

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

This approach allows you also build a string result from a list of objects Example

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

Here the reduce function allows you to have some initial value to which you want to append new string Example:

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

Testing both approaches suggested in Shail016 and bpedroso answer (https://stackoverflow.com/a/24883180/2832140), the simple StringBuilder + append(String) within a for loop, seems to execute much faster than list.stream().map([...].

Example: This code walks through a Map<Long, List<Long>> builds a json string, using 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());
}

On my dev VM, junit usually takes between 0.35 and 1.2 seconds to execute the test. While, using this following code, it takes between 0.15 and 0.33 seconds:

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

A clean way to do this is by mapping the elements of the list to string and then using the joining operation in Collectors class.

List<Integer> ls = new ArrayList<Integer>();
ls.add(1);
ls.add(2);
ls.add(3);
String s = ls.stream().map(Object::toString).collect(Collectors.joining(","));
1

Using Stream

    List<Integer> list = Arrays.asList(1, 2, 3);
    String s = list.stream().map((i)->i.toString()).collect(Collectors.joining());
Karim Omaya
  • 841
  • 7
  • 7
0
String actual = list.stream().reduce((t, u) -> t + "," + u).get();
Joe Almore
  • 4,036
  • 9
  • 52
  • 77
  • 7
    Usually it is a good idea to add an explanation to your post that talks about how the code works. This allows new developers to understand what the code does. – Caleb Kleveter Jan 05 '17 at 13:21
  • 1
    @CalebKleveter it reduces a `List` to a single `String` and separate each element with a comma (`,`). – Joe Almore Jan 05 '17 at 13:47
  • 2
    Your solution ONLY works if list is a List. OP didn't specify such class. In his question even a List is pointed. Your code doesn't compile in that case. – RubioRic Nov 08 '17 at 15:15
0

I'm going to use the streams api to convert a stream of integers into a single string. The problem with some of the provided answers is that they produce a O(n^2) runtime because of String building. A better solution is to use a StringBuilder, and then join the strings together as the final step.

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

You can take this process a step further by converting the collection of object to a single string.

// 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

Can we try this.

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
  • 3,004
  • 33
  • 29
  • Your solution ONLY works if list is a List. OP didn't specify such class. In his question even a List is pointed. – EduardoMaia Oct 19 '21 at 18:59
-2

Also, you can do like this.

    List<String> list = Arrays.asList("One", "Two", "Three");
    String result = String.join(", ", list);
    System.out.println(result);
Evgenii
  • 447
  • 1
  • 10
  • 26
  • Your solution ONLY works if list is a List. OP didn't specify such class. In his question even a List is pointed. – RubioRic Nov 08 '17 at 15:20
-2

With Java 8+

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

Not the most efficient, but it is a solution with a small amount of code.

James Wierzba
  • 16,176
  • 14
  • 79
  • 120