Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a map with composite key with Java 8 on the fly

I want to create a map like below->

Map<Pair<MyClass.a, MyClass.b>, MyClass>>.

I have a list of object ->

List<MyClass>

Here Pair is a class, already in my project, so I wanted to use it.

I need help to create it from a Java 8 stream.

I did try ::

ls.stream().collect(Collectors.toMap(new Pair(MyClass.a, MyClass.b), MyClass));

But I am getting an error. I am new to Java 8 and trying to learn it.

Adding an example :

class Person {
   String name ;
   int age ;
   // Some other variables
}

I have a list of List<Person>.

In my requirement I need a key = {name, age}, using the pair class.

class Pair<T,U> {     
    Pair(T t, U u) {
        this.t = t
        this.u = u
    }

    // Overridden hashCode && equals methods
}

Now I want to create a map like Map<Pair<String, Int>, Person>

I was getting a compiler error that said "Not a functional interface".

I am sure there must be a way via java 8 stream and collect.

like image 323
amu61 Avatar asked Sep 06 '25 20:09

amu61


1 Answers

In order to create a function, you must use a lambda expression. It’s not sufficient an write an expression like new Pair(MyClass.a, MyClass.b), instead, you specify a function having a parameter, that will be a Person instance, i.e. p -> new Pair<>(p.name, p.age). Alternatively you may make the parameter explicit: (Person p) -> new Pair<>(p.name, p.age).

For your Map creation operation, you have to decide, what you want. E.g.,

Map<Pair<String, Integer>, List<Person>> map
    = list.stream().collect(Collectors.groupingBy(p -> new Pair<>(p.name, p.age)));

will map each key to a list of all Person instances having that name/age combination.

In contrast

Map<Pair<String, Integer>, Person> map
    = list.stream().collect(Collectors.toMap(p -> new Pair<>(p.name, p.age), p -> p));

will map the name/age pairs to a single Person instance, but throw an exception, if there is more than one with the same key. You can specify a function to resolve such conflicts, e.g.

Map<Pair<String, Integer>, Person> map = list.stream().collect(
    Collectors.toMap(p -> new Pair<>(p.name, p.age), p -> p, (first, next) -> first));

will keep the first, whereas

Map<Pair<String, Integer>, Person> map = list.stream().collect(
    Collectors.toMap(p -> new Pair<>(p.name, p.age), p -> p, (prev, last) -> last));

will overwrite the previous occurrence, ending up with the last Person instance for each name/age combination.

like image 111
Holger Avatar answered Sep 10 '25 01:09

Holger