87

I'm trying to come up with a more succinct expression for the "keyMapper" function parameter in the following Collectors.toMap() call:

List<Person> roster = ...;

Map<String, Person> map = 
    roster
        .stream()
        .collect(
            Collectors.toMap(
                new Function<Person, String>() { 
                    public String apply(Person p) { return p.getLast(); } 
                },
                Function.<Person>identity()));

It seems that I should be able to inline it using a lambda expression, but I cannot come up with one that compiles. (I'm quite new to lambdas, so that's not much of a surprise.)

Thanks.

--> Update:

As noted in the accepted answer

Person::getLast

is what I was looking for, and is something I had tried. However, the BETA_8 nightly build of Eclipse 4.3 was the problem -- it flagged that as wrong. When compiled from the command-line (which I should have done before posting), it worked. So, time to file a bug with eclipse.org.

Thanks.

Michael Lihs
  • 7,460
  • 17
  • 52
  • 85
pfurbacher
  • 1,789
  • 3
  • 15
  • 23
  • 1
    Note also a static import for Collectors.toMap will make the expression shorter still, NetBeans doesn't seem to be importing these for me though however. – Brett Ryan Apr 29 '14 at 06:23
  • Just stumbled over this question. Your problem was not only Eclipse 4.3, it was also JDK 8 before u112, which had similar problems. HTH. – Benjamin Marwell Aug 03 '18 at 06:05

3 Answers3

206

You can use a lambda:

Collectors.toMap(p -> p.getLast(), Function.identity())

or, more concisely, you can use a method reference using :::

Collectors.toMap(Person::getLast, Function.identity())

and instead of Function.identity, you can simply use the equivalent lambda:

Collectors.toMap(Person::getLast, p -> p)

If you use Netbeans you should get hints whenever an anonymous class can be replaced by a lambda.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • 2
    You can make your expressions even shorter by removing parentheses as they are not needed for single arguments, i.e. `Collectors.toMap(Person::getLast, p -> p)`. – Brett Ryan Apr 29 '14 at 06:22
  • 1
    I'm also pretty sure the type argument for the identity function is not required as it can be inferred. `Collectors.toMap(Person::getLast, Function.identity())` – GuiSim Jun 27 '14 at 14:12
  • @GuiSim I think it is now the case indeed - it was a bug in the earlier versions of Java 8. – assylias Jun 27 '14 at 16:11
  • 2
    Hehe, it seems the p -> p is optimized somehow, no new compiled anonymous class is built. I guess it uses Function.identity() internally, instead of creating a useless class. – Vlasec Feb 02 '15 at 10:11
  • 1
    Important caveat! If your List contains null values, you will get an exception here because of how Collectors.toMap is implemented, even though a map with null values is normally perfectly acceptable. Surely in this case you would get an NPE anyway when accessing Person::getLast but there are many other cases where your key might be composed differently but you would still get the exception. See https://stackoverflow.com/questions/24630963/java-8-nullpointerexception-in-collectors-tomap – Sebastiaan van den Broek May 04 '18 at 09:01
31
List<Person> roster = ...;

Map<String, Person> map = 
    roster
        .stream()
        .collect(
            Collectors.toMap(p -> p.getLast(), p -> p)
        );

that would be the translation, but i havent run this or used the API. most likely you can substitute p -> p, for Function.identity(). and statically import toMap(...)

aepurniet
  • 1,719
  • 16
  • 24
7

We can use an optional merger function also in case of same key collision. For example, If two or more persons have the same getLast() value, we can specify how to merge the values. If we not do this, we could get IllegalStateException. Here is the example to achieve this...

Map<String, Person> map = 
roster
    .stream()
    .collect(
        Collectors.toMap(p -> p.getLast(),
                         p -> p,
                         (person1, person2) -> person1+";"+person2)
    );
KayV
  • 12,987
  • 11
  • 98
  • 148
  • 1
    I'm sorry but what does it mean ? `(person1, person2) -> person1+";"+person2)` – Thach Huynh Nov 22 '17 at 07:21
  • 1
    If the mapped keys contains duplicates (according to Object.equals(Object)), the value mapping function is applied to each equal element, and the results are merged using the provided merging function. Pls refer to the link https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-java.util.function.BinaryOperator- – KayV Nov 22 '17 at 07:27