JB Header
Java 8 Joining with Collectors | Collectors.joining method tutorial with examples
Java 8 Joining with Collectors tutorial explains how to use the predefined Collector returned by java.util.Stream.Collectors class' joining() method with examples. The 'joining collector' collects elements of a stream by concatenating them into a single String. Collectors class provided 3 overloaded joining() methods returning 3 variants of joining collectors. All variants return concatenated strings but with minor differences.

In this tutorial I will explain the three overloaded joining() methods starting with their formal definition, then explain their working, and finally shows the methods' usage via Java code examples. (Note - This tutorial assumes that you are familiar with basics of Java 8 CollectorsRead Tutorial explaining basics of Java 8 Collectors.) Predefined Collector returned by Collectors.joining() method with no arguments Collectors.joining() method is defined with the following signature -
public static Collector<CharSequence, ?, String> joining()
Where,
     - output is a Collector, which collects a Stream of elements of type CharSequence, with its finisherClick to Read tutorial on 4 components of Collectors incl. 'finisher' returning the 'collected' value of type String

The Collector returned by Collectors.joining() method returns a String which is the concatenation of all elements of the Stream. An important thing to keep in mind regarding the joining collector is that it needs a stream of type java.lang.CharSequence to be fed as input to it. In other words, joining collector can only concatenate streams whose elements are subclasses of CharSequence interface. String, StringBuffer, StringBuilder are some of the subclasses of CharSequence which you will be using in most of joining scenarios. It is also worthwhile to note that internally the joining collector uses a StringBuilder instance to concatenate the stream elements.
So, given a stream of type String with 3 values “a”, “b”, “c” and you intend to create a concatenated string with value “abc”,i.e. a straightforward concatenation of the stream elements, then Collectors.joining() method is what you need to pass to your stream’s terminal operationClick to Read Tutorial explaining intermediate & terminal Stream operations collect().

idea icon What if your Stream elements aren't subclasses of CharSequence ?
In that case you need to convert the stream elements to their corresponding CharSequence (subclass of CharSequence rather) equivalents using the Stream.map() method with a Function<T,CharSequence> instance. You can also use the toString() method in your Function definition to get the String equivalents of your stream elements, i.e. if the elements' class has the required toString() format. Mapping to toString() will work because String class implements CharSequence.
Java 8 code example showing Collectors.joining() method's usage
Java 8 code example showing Collectors.joining() usage
package com.javabrahman.java8.collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class JoiningWithCollectors {

  public static void main(String args[]){
    String joinedStr = 
        Stream.iterate(new Integer(0), (Integer integer) -> integer + 1)
              .limit(5)
              .map(integer -> integer.toString())
              .collect(Collectors.joining());
    System.out.println("Joined String: "+joinedStr);
  }

}
 OUTPUT of the above code
Joined String:  01234
Explanation of the code Low readability of concatenated String returned by joining collector If you notice carefully in the output of the above joining collector, then you will see that the elements which are concatenated together make one single whole string. It is difficult to find out which individual strings or characters were concatenated together to create the final joined string. Java designers noticed this aspect as well, and they have designed 2 overloaded joining() methods which make the concatenated string more comprehensible. The first of these methods allows inserting a delimiter between joined elements, while the second method puts a prefix and suffix for the entire joined string. Let us take a look at how these 2 overloaded Collectors.joining() methods work. Joining Collector which adds a delimiter between concatenated elements Overloaded Collectors.joining() method which accepts a delimiter is defined with the following signature -
public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
Where,
     - delimiter is the only input parameter of type CharSequence
     - output is a Collector, acting on a Stream of elements of type CharSequence, with its finisherClick to Read tutorial on 4 components of Collectors incl. 'finisher' returning the 'collected' value of type String

The Collector returned by Collectors.joining() method returns a String which is the concatenation of all elements of the Stream, with the delimiter passed as input inserted between concatenated elements.

So, given a stream of type String with 3 values “a”, “b”, “c”, and you intend to create a concatenated string with value “a,b,c”, then Collectors.joining(",") method is what you need to pass to your stream's terminal operation collect().
Java 8 code example for Collectors.joining() method with a delimiter (Note - The class definition and imports are same as Collectors.joining() example above and hence are not repeated again for brevity)
Java 8 code example for Collectors.joining() with delimiter
package com.javabrahman.java8.collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class JoiningWithCollectors {

  public static void main(String args[]){
    String joinedStr = 
        Stream.iterate(new Integer(0), (Integer integer) -> integer + 1)
              .limit(5)
              .map(integer -> integer.toString())
              .collect(Collectors.joining(","));
    System.out.println("Joined String: "+joinedStr);
  }

}
 OUTPUT of the above code
Joined String:  0,1,2,3,4
Explanation of the code
  • The code in the above example is exactly the same as that written for the plain joining() method that takes no input, except for the collect() operation where we pass the overloaded joining() method which takes a delimiter as input which in our case is a “,”(comma).
  • The output is as expected - 0,1,2,3,4 i.e. final joined String with “,” inserted between elements as the delimiter.
The 3rd and last variant of joining collector is quite similar to the first 2 variants, except that it adds a suffix and prefix as well to the final joined string. Let us take a look at it now. Joining Collector The Joining Collector adds a delimiter between elements, and suffix & prefix to joined String. The signature of the 3rd variant of the joining collector is as follows -
public static Collector<CharSequence, ?, String> joiningjoining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)
Where,
     - input param delimiter of type CharSequence is the delimiter which will be added between concatenated stream elements
     - input param prefix of type CharSequence will be appended before the joined String
     - input param suffix of type CharSequence will be appended after the joined String
     - output is a Collector, acting on a Stream of elements of type CharSequence, with its finisher returning the 'collected' value of type String
The Collector returned by Collectors.joining() method returns a String which is the concatenation of all elements of the Stream, with the delimiter passed as input inserted between concatenated elements, and the prefix and suffix appended to the joined String.

So, given a stream of type String with 3 values “a”, “b”, “c”, and you intend to create a concatenated string with value “{a,b,c}”, then Collectors.joining(",","{","}") method is what you need to pass to your stream's terminal operation collect(). Java 8 code example for Collectors.joining() method with a delimiter, prefix & suffix (Note - The class definition and imports are same as Collectors.joining() example above and hence are not repeated again for brevity)
Java 8 code example for Collectors.joining() with delimiter,prefix & suffix
  public static void main(String args[]){
    String joinedStr = 
        Stream.iterate(new Integer(0), (Integer integer) -> integer + 1)
              .limit(5)
              .map(integer -> integer.toString())
              .collect(Collectors.joining(",","{","}"));
    System.out.println("Joined String: "+joinedStr);
  }
 OUTPUT of the above code
Joined String:  {0,1,2,3,4}
Explanation of the code
  • The code in the above example is exactly the same as that for the previous 2 overloaded variants of the joining() method, except for the collect() operation where we pass the 3rd variant of the joining() method which takes a delimiter, a prefix and a suffix as input parameters..
  • The output is as expected - {0,1,2,3,4} i.e. a final joined String with “,” inserted between elements as the delimiter, and “{” & “}” as prefix and suffix respectively to the final joined String.
Summary In the above tutorial we understood how the joining collector returned by java.util.Stream.Collectors.joining() method works. We looked at the 3 overloaded variants of the joining() method and understood their working by going through their method signatures and Java 8 code examples showing three methods in use.