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