Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 - chaining constructor call and setter in stream.map()

I have a class

class Foo{     String name;     // setter, getter } 

which just has a default constructor.

Then, I am trying to create a List of Foo from some string:

Arrays.stream(fooString.split(","))             .map(name -> {                 Foo x = new Foo();                 x.setName(name);                 return x;              }).collect(Collectors.toList())); 

Since there is no constructor which takes a name, I can't simply use a method reference. Of course, I could extract those three lines, with the constructor call and the setter, into a method but is there any better or concise way to do that? (without changing Foo, which is a generated file)

like image 833
user140547 Avatar asked Nov 04 '15 09:11

user140547


People also ask

Can we use map stream in Java 8?

Java 8 Stream's map method is intermediate operation and consumes single element forom input Stream and produces single element to output Stream. It simply used to convert Stream of one type to another. Let's see method signature of Stream's map method.

What is stream () method in Java?

A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result. The features of Java stream are – A stream is not a data structure instead it takes input from the Collections, Arrays or I/O channels.

Can we use stream with map in Java?

Converting only the Value of the Map<Key, Value> into Stream: This can be done with the help of Map. values() method which returns a Set view of the values contained in this map. In Java 8, this returned set can be easily converted into a Stream of key-value pairs using Set. stream() method.

What are two types of streams in Java 8?

With Java 8, Collection interface has two methods to generate a Stream. stream() − Returns a sequential stream considering collection as its source. parallelStream() − Returns a parallel Stream considering collection as its source.


2 Answers

If this happens repeatedly, you may create a generic utility method handling the problem of constructing an object given one property value:

public static <T,V> Function<V,T> create(     Supplier<? extends T> constructor, BiConsumer<? super T, ? super V> setter) {     return v -> {         T t=constructor.get();         setter.accept(t, v);         return t;     }; } 

Then you may use it like:

List<Foo> l = Arrays.stream(fooString.split(","))     .map(create(Foo::new, Foo::setName)).collect(Collectors.toList()); 

Note how this isn’t specific to Foo nor its setName method:

List<List<String>> l = Arrays.stream(fooString.split(","))     .map(create(ArrayList<String>::new, List::add)).collect(Collectors.toList()); 

By the way, if fooString gets very large and/or may contain lots of elements (after splitting), it might be more efficient to use Pattern.compile(",").splitAsStream(fooString) instead of Arrays.stream(fooString.split(",")).

like image 100
Holger Avatar answered Oct 14 '22 16:10

Holger


No, there is no better way.

The only alternative is, like you said in your question, to create a factory for Foo objects:

public class FooFactory {     public static Foo fromName(String name) {         Foo foo = new Foo();         foo.setName(name);         return foo;     } } 

and use it like this:

Arrays.stream(fooString.split(",")).map(FooFactory::fromName).collect(toList()); 

If there are a lot of names to split, you can use Pattern.compile(",").splitAsStream(fooString) (and store the compiled pattern in a constant to avoid recreation) instead of Arrays.stream(fooString.split(",")).

like image 40
Tunaki Avatar answered Oct 14 '22 16:10

Tunaki