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()
.
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
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); } }
- In the
main()
method ofJoiningWithCollectors
class, first an infinite streamClick to Read Tutorial on creating infinite streams using iterate & generate methods is created usingStream.iterate()
method. The initial value of thisStream
is set asInteger(0)
and then each subsequent element is obtained by adding1
. - Infinite stream of integers is then limited to
5
elements using the Stream.limit(5)Click to Read Tutorial explaining how Stream.limit() is used to slice streams method. - Next,
Stream
elements are mapped to theirString
equivalents using a pipelinedClick to Read Tutorial explaining concept of Pipelines in ComputingStream.map()
method which accepts as input aFunction<Integer,String>
which is specified using its lambda expressioninteger -> integer.toString()
. - Lastly, on this stream of Strings the
collect()
operation is invoked withCollectors.joining()
returning the desired joiningCollector
instance. - The final collected value returned from the
Stream
is01234
which is then printed as output.
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)
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); } }
- The code in the above example is exactly the same as that written for the plain
joining()
method that takes no input, except for thecollect()
operation where we pass the overloadedjoining()
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 joinedString
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
which 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)
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); }
- The code in the above example is exactly the same as that for the previous 2 overloaded variants of the
joining()
method, except for thecollect()
operation where we pass the 3rd variant of thejoining()
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 joinedString
with“,”
inserted between elements as the delimiter, and“{”
&“}”
as prefix and suffix respectively to the final joinedString
.
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.
Understanding Basics of Java 8 CollectorsClick to Read Tutorial explaining basics of Java 8 CollectorsCollectors.groupingBy()Click to Read Tutorial on Grouping with CollectorsCollectors.partitioningBy()Click to Read Partitioning using Collectors TutorialCollectors.counting()Click to Read Counting with Collectors Tutorial Collectors.maxBy()/minBy()Click to Read Tutorial on finding max/min with CollectorsCollectors.joining()Click to Read Tutorial on joining as a String using CollectorsCollectors.collectingAndThen()Click to Read Tutorial on collectingAndThen CollectorCollectors.averagingInt() /averagingLong() /averagingDouble()Click to Read Tutorial on Averaging CollectorCollectors.toCollection()Click to Read Tutorial on Collectors.toCollection CollectorCollectors.mapping()Click to Read Tutorial on Mapping Collector
