Why StringJoiner when we already have StringBuilder?
Asked Answered
I

5

76

I recently encountered with a Java 8 class StringJoiner which adds the String using the delimiters and adds prefix and suffix to it, but I can't understand the need of this class as it also uses StringBuilder at the backend and also performs very simple operation of appending the Strings.

Am I missing something by not actually understanding the real purpose of this class?

Idealist answered 17/12, 2014 at 9:45 Comment(1)
Being simple to implement does not preclude being useful when carried out as a class on its own. Especially when considering how often application programmers had to do similar by hand (or via 3rd party library).Farreaching
D
79

StringJoiner is very useful, when you need to join Strings in a Stream.

As an example, if you have to following List of Strings:

final List<String> strings = Arrays.asList("Foo", "Bar", "Baz");

It is much more simpler to use

final String collectJoin = strings.stream().collect(Collectors.joining(", "));

as it would be with a StringBuilder:

final String collectBuilder =
    strings.stream().collect(Collector.of(StringBuilder::new,
        (stringBuilder, str) -> stringBuilder.append(str).append(", "),
        StringBuilder::append,
        StringBuilder::toString));

EDIT 6 years later As noted in the comments, there are now much simpler solutions like String.join(", ", strings), which were not available back then. But the use case is still the same.

Disburse answered 17/12, 2014 at 9:58 Comment(5)
Note that these two examples are not the same! The first generates the string "foo, Bar, Baz", and the second generates "foo, Bar, Baz, ".Memorize
@PaulWagland Yes, that's the extra beauty of the .joining. Usually, you don't need the last comma...Tatouay
@Tatouay agree totally, fixing that in the streaming case also makes the already complicated statement even more complicated!Memorize
String.join(", ", strings) is a quicker verrsion for strings.stream().collect(Collectors.joining(", "))Frechette
What about: list.stream().map(Util::quote).collect(Collectors.joining(",")) ?Kitchens
F
28

The examples on the StringJoiner Javadoc are very good at covering this. The whole point to to abstract away the choice of seperator from the act of adding entries. e.g. you can create a joiner, specify the seperator to use and pass it to a library to do the adding of elements or visa versa.

The String "[George:Sally:Fred]" may be constructed as follows:

StringJoiner sj = new StringJoiner(":", "[", "]");
sj.add("George").add("Sally").add("Fred");
String desiredString = sj.toString();

A StringJoiner may be employed to create formatted output from a Stream using Collectors.joining(CharSequence). For example:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
String commaSeparatedNumbers = numbers.stream()
    .map(i -> i.toString())
    .collect(Collectors.joining(", "));
Frederickson answered 17/12, 2014 at 9:49 Comment(2)
Peter, have you done any benchmarking on the performance of StringBuilder vs. StringJoiner? Which one would you prefer from that regard?Munitions
@Simeon I would use String joiner if I want an implicit separator between each thing I append and a string builder if not. It's not about performance but making the desired action clearer.Frederickson
C
16

It may simplify your code in some use cases:

List<String> list = // ...;

// with StringBuilder
StringBuilder builder = new StringBuilder();
builder.append("[");
if (!list.isEmpty()) {
    builder.append(list.get(0));
    for (int i = 1, n = list.size(); i < n; i++) {
        builder.append(",").append(list.get(i));
    }
}
builder.append("]");

// with StringJoiner
StringJoiner joiner = new StringJoiner(",", "[", "]");
for (String element : list) {
    joiner.add(element);
}
Clarissaclarisse answered 17/12, 2014 at 9:53 Comment(1)
It would be great if StringJoiner would have a method receiving a List or Collection and do the iteration + adding each element internally.Taeniasis
B
13

StringJoiner is a kind of a Collector, although it doesn't implement the Collector interface. It just behaves as such. Besides you can pass delimiter, prefix and suffix, the StringJoiner may be employed to create formatted output from a Stream invoking Collectors.joining(CharSequence).

This is especially useful when working with parallel streams, because at some point the batches that are being process in parallel will need to be joined and this is where the StringJoiner takes place.

Batruk answered 17/12, 2014 at 9:50 Comment(1)
Not exactly. It is a low-level utility component suitable for use from a Collector (joining() does in fact use it), but also suitable outside the world of Streams too. People write these kinds of loops all the time.Embalm
I
7

StringJoiner is far simpler than using StringBuilder. A very simple code is like

StringJoiner sj = new StringJoiner(",");
        sj.add("aaa");
        sj.add("bbb");
        sj.add("ccc");
        String result = sj.toString(); //aaa,bbb,ccc
Integrate answered 22/6, 2018 at 13:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.