Collectors.toMap()的keyMapper参数--是否有更简洁的表达方式?

89

我试图为以下Collectors.toMap()调用中的"keyMapper"函数参数想出更简洁的表达方式:

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()));

看起来我应该能够使用lambda表达式进行内联,但我无法想出一个可以编译的lambda表达式。(我对lambda表达式很新,所以这并不令人感到意外。)

谢谢。

--> 更新:

正如被接受的答案中所提到的

Person::getLast

这正是我正在寻找的内容,而且我之前也试过。然而,Eclipse 4.3 的 BETA_8 每夜构建版就是问题所在——它将其标记为错误。当从命令行编译时(在发布之前应该这样做),它就可以工作了。因此,现在是向eclipse.org提交bug报告的时候了。

谢谢。


1
请注意,静态导入Collectors.toMap可以使表达式更简短。然而,NetBeans似乎没有为我自动导入这些内容。 - Brett Ryan
刚刚偶然看到这个问题。你的问题不仅仅是Eclipse 4.3,还有JDK 8在u112之前也有类似的问题。希望能对你有所帮助。 - Benjamin Marwell
3个回答

208

您可以使用lambda表达式:

Collectors.toMap(p -> p.getLast(), Function.identity())
或者更简洁地说,您可以使用::来使用方法引用
Collectors.toMap(Person::getLast, Function.identity())

而不是使用 Function.identity,您可以简单地使用等效的 lambda:

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

如果您使用Netbeans,当一个匿名类可以被lambda替换时,您应该会得到提示。


2
你可以通过去掉括号来进一步简化表达式,因为对于单个参数而言它们是不必要的,例如 Collectors.toMap(Person::getLast, p -> p) - Brett Ryan
1
我也非常确定身份函数的类型参数不是必需的,因为它可以被推断出来。Collectors.toMap(Person::getLast, Function.identity()) - GuiSim
@GuiSim 我认为现在的情况确实是这样的 - 这是Java 8早期版本中的一个错误。 - assylias
2
嘿嘿,看起来 p -> p 有所优化,没有新的编译匿名类被构建。我猜它在内部使用了 Function.identity(),而不是创建一个无用的类。 - Vlasec
1
重要提示!如果您的List包含null值,由于Collectors.toMap的实现方式,您将在此处收到异常,即使具有null值的映射通常是完全可接受的。当访问Person :: getLast时,在这种情况下肯定会收到NPE,但还有许多其他情况,您的键可能组合不同,但仍会收到异常。请参见https://dev59.com/tWAf5IYBdhLWcg3wcyer - Sebastiaan van den Broek

32
List<Person> roster = ...;

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

那将是翻译,但我没有运行过这个或使用API。很可能你可以将p -> p替换为Function.identity(),并静态导入toMap(...)。


在List<Person>中,是否有一种方法可以合并相似的值以避免连接它们而不是重复项? - Lovegiver

7
我们可以在出现相同键冲突的情况下使用可选的合并函数。例如,如果两个或更多人有相同的getLast()值,我们可以指定如何合并这些值。如果不这样做,我们可能会得到IllegalStateException。以下是实现此功能的示例...
Map<String, Person> map = 
roster
    .stream()
    .collect(
        Collectors.toMap(p -> p.getLast(),
                         p -> p,
                         (person1, person2) -> person1+";"+person2)
    );

1
不好意思,这是什么意思?(person1,person2) -> person1 +";" + person2) - Thach Huynh
1
如果映射键包含重复项(根据Object.equals(Object)定义),则对每个相等的元素应用值映射函数,并使用提供的合并函数合并结果。请参考链接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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接