String.join for Iterable<T>, not just CharSequence

Stuart Marks stuart.marks at oracle.com
Thu Apr 11 02:02:38 UTC 2019


Hi Michael,

This seems focused rather too narrowly on the task of joining strings obtained 
by applying exactly one mapper. It doesn't help if the task is something other 
than joining, and it doesn't help if there is something other than exactly one 
mapper. You'd then have to fall back to using a stream, which in fact isn't all 
that bad in the first place.

Compare the code with your proposed enhancement,

   String.join(";", List.of(1.234, 2.345, 3.456),
               NumberFormat.getInstance()::format);

to the stream version you suggested:

   Stream.of(1.234, 2.345, 3.456)
         .map(NumberFormat.getInstance()::format)
         .collect(joining(";"));

(using static imports, and with line breaks for clarity). This isn't much of a 
difference.

But also note, if my IterableOnce proposal (JDK-8148917) gets in (yes, I need to 
pick this back up), it would be possible to write:

   String.join(";", Stream.of(1.234, 2.345, 3.456)
                          .map(NumberFormat.getInstance()::format));

The Path example is somewhat more cumbersome, because of the need to convert the 
Path (as an Iterable) to a Stream:

   StreamSupport.stream(path.spliterator(), false)
                .map(Object::toString)
                .collect(joining(";"));

Now this isn't terrible, but it does seem more cumbersome than it ought to be. 
In particular having to create a Spliterator to convert an Iterable to a Stream 
is pretty non-obvious. It suggests to me that it would be better to work on 
making it easier to convert an Iterable to a Stream instead of adding a mapper 
to String.join().

However, we're probably not going to add a default method stream() to the 
Iterable interface at this point. It's just too high up in the hierarchy to be 
safe. See this Stack Overflow answer from Brian [1] and the Lambda EG discussion 
on the topic [2]. But with the benefit of several years of experience with this 
stuff, it might be feasible to create a smoother path with the judicious 
addition of factory methods.

s'marks

[1] https://stackoverflow.com/a/23177907/1441122

[2] 
http://mail.openjdk.java.net/pipermail/lambda-libs-spec-experts/2013-June/001910.html






On 4/10/19 2:48 AM, Michael Rasmussen wrote:
> Hi
> 
> I was wonder if there had been any considerations adding an overloaded String.join method, that take an Iterable<T> as argument, and a Function<T, CharSequence) as a mappingFunction?
> This would allow you easily join an Iterable of items that are not Strings, but you could easily map to a String by calling toString or another method.
> 
> Example usage: String.join(";", List.of(1.234, 2.345, 3.456), NumberFormat.getInstance()::format);
> 
> I know the same thing is doable using a Stream, for instance the above like: Stream.of(1.234, 2.345, 3.456).map(NumberFormat.getInstance()::format).collect(Collectors.joining(";"));
> The String.join version just seems more convenient and easier to read. Also, for non-collection Iterable object (i.e. that doesn't have a .stream() method), such as java.nio.file.Path, the Stream version becomes rather cumbersome.
> for instance joining path elements with a different delimiter would be as easy as String.join(";", path, Object::toString);
> 
> Implementation wise, the existing implementation only requires slight modification to apply the mapping function, and the existing method then just becomes a wrapper:
> 
> public static <T> String join(CharSequence delimiter,
>                                Iterable<T> elements,
>                                Function<T, ? extends CharSequence> mappingFunction) {
>    Objects.requireNonNull(delimiter);
>    Objects.requireNonNull(elements);
>    Objects.requireNonNull(mappingFunction);
>    StringJoiner joiner = new StringJoiner(delimiter);
>    for (T elem : elements) {
>      joiner.add(mappingFunction.apply(elem));
>    }
>    return joiner.toString();
> }
> 
> public static String join(CharSequence delimiter,
>                            Iterable<? extends CharSequence> elements) {
>    return join(delimiter, elements, Function.identity());
> }
> 
> Kind regards
> Michael Rasmussen
> 


More information about the core-libs-dev mailing list