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

Reply via email to