fastcodejava
fastcodejava

Reputation: 41127

Map key value to an object in Java 8 stream

Let's say I have this code:

Map<String, String> map;
// later on
map.entrySet().stream().map(MyObject::new).collect(Collectors.toList());

And I have a MyObject Constructor which takes two arguments of type String. I want to be able to do this but I cannot. I know I can do e -> new MyObject(e.getKey(), e.getValue()) but prefer MyObject::new.

Similar code works for Set<String> and List<String> with one argument constructor of MyObject class.

Upvotes: 6

Views: 22559

Answers (4)

Eugene Barnett
Eugene Barnett

Reputation: 61

Add a Map.Entry constructor

class MyObject {
        private final String k;
        private final String v;

        MyObject(String s1, String s2) {
            k = s1;
            v = s2;
        }

        MyObject(Map.Entry<String, String> e) {
            this(e.getKey(), e.getValue());
        }

        public String toString() {
            return "key: " + k + ' ' + "value: " + v;
        }
    }

You will be able to call

List<MyObject> myObjectList = map.entrySet().stream().map(MyObject::new).collect(Collectors.toList());

Upvotes: 2

Andrew
Andrew

Reputation: 49656

The problem with the constructor is that it defines two parameters, while Function#apply demanded by Stream#map accepts only one.

You could write a static factory method for MyObject

class MyObject {
    public static MyObject of(Map.Entry<String, String> e) {
        return new MyObject(e.getKey(), e.getValue());
    }
}

and refer to it like

map(MyObject::of)

Before you do so, ask yourself if one pretty-looking line in a plain processing chain somewhere is worthy of a new constructor or utility method.

Upvotes: 2

janos
janos

Reputation: 124844

map.entrySet().stream().map(MyObject::new).collect(Collectors.toList()));

And I have a MyObject Constructor which takes two arguments of type String. I want to be able to do this but I cannot.

In map.entrySet().stream().map(...), Java is expecting a Function, mapping one value to another value. One value. From the stream, the function receives a value of type Map.Entry<String, String>, but your constructor takes two String arguments. Java doesn't automagically expand a Map.Entry<String, String> to a pair of Strings to pass to your constructor. You have to do that unwrapping manually.

Upvotes: 3

Ousmane D.
Ousmane D.

Reputation: 56499

use a lambda:

map.entrySet()
   .stream()
   .map(e -> new MyObject(e.getKey(), e.getValue()))
   .collect(Collectors.toList());

otherwise the only way to use a method reference is by creating a function as such:

private static MyObject apply(Map.Entry<String, String> e) {
      return new MyObject(e.getKey(), e.getValue());
}

then do something like:

map.entrySet()
   .stream()
   .map(Main::apply)
   .collect(Collectors.toList());

Where Main is the class containing the apply method.

Upvotes: 11

Related Questions