In the case Foo::bar returns an instance of Foo again, ie. you need to transform T into T again, then you can use List::replaceAll which uses UnaryOperator<T>, therefore each item is replaced by one of a same type. This solution mutates the original list.
List<String> list = Arrays.asList("John", "Mark", "Pepe");
list.replaceAll(s -> "hello " + s);
If you want to transform T into R, all you can do is to either use your current solution with a sequence of stream() -> map() -> collect() method calls or a simple iteration.
A static method wrapping this would do the same as well. Note that you cannot create a Stream from both Collection and Iterable using the same way. Feel free to pass also your custom Collector.
T is a generic type of an input Collection or Iterable.
R is a generic type of the mapping function result (mapping from T to R)
From Collection<T>
List<Bar> listBarFromCollection = mapApply(collectionFoo, Foo::bar, Collectors.toList());
static <T, R> List<R> mapApply(Collection<T> collection, Function<T, R> function) {
return collection.stream()
.map(function)
.collect(Collectors.toList());
}
From Iterable<T>
List<Bar> listBarFromIterable = mapApply(iterableFoo, Foo::bar, Collectors.toList());
static <T, R> List<R> mapApply(Iterable<T> iterable, Function<T, R> function) {
return StreamSupport.stream(iterable.spliterator(), false)
.map(function)
.collect(Collectors.toList());
}
... with a Collector:
If you want to pass a custom Collector, it would be Collector<R, ?, U> collector and the return type of the method U instead of List<R>. As @Holger pointed out, passing a Collector to a method would not much differ from calling an actual stream() -> map() -> collect().