合并两个地图时为 Map.Entry::getKey 发出编译错误

发布于 2025-01-15 05:39:41 字数 1091 浏览 1 评论 0原文

每当我在我的公共方法的流中使用 Map.Entry::getKey 时,我都会遇到一个关于我的方法不是静态 的问题。我什至尝试将我的方法设置为静态,但它不起作用。

下面是我使用 Map.Entry()::getKey() 得到的编译错误

Non-static method cannot be referenced from a static context

我的代码

/***
 * Merge 2 Maps and add the values together if they are in both maps
 * 
 * firstMap = {"Eggs": 3, "Cereal": 1}
 * secondMap = {"Eggs": 10, "Coke": 23, "Cereal": 1}
 * 
 * Answer = {"Eggs": 13, "Coke": 23, "Cereal": 2}
 * Notice that the Eggs are now 13
 * 
 * @param firstMap
 * @param secondMap
 * @return
 */
public Map<String, Integer> mergeAndAddValues(Map<String, String> firstMap, Map<String, String> secondMap) {

    return Stream.of(firstMap, secondMap)
            .flatMap(map -> map.entrySet().stream())
            .collect(Collectors.toMap(
                       Map.Entry()::getKey,
                       Map.Entry::getValue,
                       Integer::sum,
                       HashMap::new));
}

Whenever I use Map.Entry::getKey in my streams for my public methods, I get an issue around my method not being static. I even tried making my method static, and it didn't work.

Below is the compile error I am getting from using Map.Entry()::getKey() :

Non-static method cannot be referenced from a static context

My code

/***
 * Merge 2 Maps and add the values together if they are in both maps
 * 
 * firstMap = {"Eggs": 3, "Cereal": 1}
 * secondMap = {"Eggs": 10, "Coke": 23, "Cereal": 1}
 * 
 * Answer = {"Eggs": 13, "Coke": 23, "Cereal": 2}
 * Notice that the Eggs are now 13
 * 
 * @param firstMap
 * @param secondMap
 * @return
 */
public Map<String, Integer> mergeAndAddValues(Map<String, String> firstMap, Map<String, String> secondMap) {

    return Stream.of(firstMap, secondMap)
            .flatMap(map -> map.entrySet().stream())
            .collect(Collectors.toMap(
                       Map.Entry()::getKey,
                       Map.Entry::getValue,
                       Integer::sum,
                       HashMap::new));
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

好菇凉咱不稀罕他 2025-01-22 05:39:41

方法参数的类型为 Map,返回类型为 Map

为了修复您的方法,您需要将 Map.Entry 类型的条目强制转换为条目 Map.Entry<字符串,整数>

这可以通过在嵌套流内应用map()操作来完成。静态方法 Map.entry() 用于基于现有条目创建一个新条目。我假设所有字符串仅由数字组成,否则您需要在解析它们之前应用额外的清除。

public Map<String, Integer> mergeAndAddValues(Map<String, String> firstMap,
                                              Map<String, String> secondMap) {

    return Stream.of(firstMap, secondMap)
            .flatMap(map -> map.entrySet().stream()
                    .map(entry -> Map.entry(entry.getKey(),
                            Integer.valueOf(entry.getValue()))))
            .collect(Collectors.toMap(
                       Map.Entry::getKey,
                       Map.Entry::getValue,
                       Integer::sum));
}

注意Collectors.toMap的一种风格,不需要mapFactory(即Collectors.toMap的版本)使用仅接受三个参数)是因为默认情况下您将获得一个 HashMap 作为结果。

手动提供 HashMap::new 不会给你带来任何好处,相反,你的代码会变得更加僵化,并且如果将来 HashMap 将被更高性能的通用代码取代Map 的目的实现,您将不能免费获得它,因为您的代码需要更改。

我什至尝试使我的方法静态......
我从使用 Map.Entry()::getKey()

无法从静态上下文引用非静态方法

此问题与静态或实例方法无关,不幸的是,这是一种罕见的情况当错误消息不是很有帮助时。如果您用 lambda 替换这些方法引用,则编译器将正确指出类型 StringInteger 不兼容。

并且不需要使用语法进行奇怪的操作,我建议您熟悉 lambda方法引用

lambda 表达式方法引用都用于提供函数接口的实现,该接口定义了一个且仅一个 >抽象方法。即lambda方法引用都应该实现这个单一方法

Collectors.toMap() 期望的所有参数都是 Java 中内置的函数接口

当由函数接口定义的行为植入已经存在的情况下(即您在某处有一个方法可以执行接口中声明的方法预期执行的操作) ,您可以通过方法引用来使用它。

有四种方法引用(引自上面引用的Oracles教程):

  • 对静态方法的引用 ContainingClass::staticMethodName
  • 引用特定对象的实例方法 containingObject::instanceMethodName
  • 引用特定类型的任意对象的实例方法 ContainingType::methodName
  • 引用构造函数ClassName::new

请注意,方法引用的语法不需要括号 >,既不在类型之后,也不在方法名称之后。

因此,正确的语法是 Map.Entry::getKey,这是对特定类型的任意对象的实例方法的引用 。即 Map.Entry 类型实际上意味着流的一个元素(一个对象,而不是一个接口)。

提醒: Entry 是一个嵌套接口,表示键值对,在 内部定义地图接口。因此,引用它的正确语法是Map.Entry(不带任何括号)。

getKeyEntry 接口声明的实例方法之一。

回顾一下

定义函数的正确语法(这是标准函数接口,预期作为第一个参数 Collectors.toMap() 使用方法引用是:

Map.Entry::getKey

双冒号之前的第一部分是将传递给的对象的类型 >函数。第二部分是方法名称

在 Java 中,从不使用括号(不要混淆对的引用)。带有构造函数调用的类型)。在方法名称之后。括号被删除是因为语言的设计方式,我猜是因为方法引用旨在简洁和富有表现力。

The method arguments are of type Map<String, String> and the return type is Map<String, Integer>.

In order to fix your method, you need to coerce entries that are of type Map.Entry<String, String> into entries Map.Entry<String, Integer>.

It could be done by applying map() operation inside the nested stream. Static method Map.entry() is used to create a new entry based on an existing one. I assume that all string are comprised of digits only, otherwise you need to apply an additional clearing before parsing them.

public Map<String, Integer> mergeAndAddValues(Map<String, String> firstMap,
                                              Map<String, String> secondMap) {

    return Stream.of(firstMap, secondMap)
            .flatMap(map -> map.entrySet().stream()
                    .map(entry -> Map.entry(entry.getKey(),
                            Integer.valueOf(entry.getValue()))))
            .collect(Collectors.toMap(
                       Map.Entry::getKey,
                       Map.Entry::getValue,
                       Integer::sum));
}

Note a flavor of Collectors.toMap that doesn't require a mapFactory (i.e. a version of Collectors.toMap that accepts only three arguments) is used because you'll get a HashMap as a result by default.

Providing HashMap::new manually doesn't byes you anything, conversely your code becomes a bit more rigid, and if in the future HashMap will get replaced with a more performant general purpose implementation of the Map you will not get it for free, for that your code will need to be changed.

I even tried making my method static ...
I am getting from using Map.Entry()::getKey()

Non-static method cannot be referenced from a static context

This issue isn't connected with static or instance method, unfortunately it's a rare case when error message isn't very helpful. If you replace these method references with lambdas, then the compiler will correctly point that types String and Integer are incompatible.

And there's no need for strange manipulations with a syntax, I kindly advise you to get familiar with these tutorials on lambdas and method references.

Both lambda expressions and method references are used to provide an implementation of a function interface, which is an interface that defines one and only one abstract method. I.e. both lambda and method reference should implement this single method.

All arguments that Collectors.toMap() expects are functional interfaces built-in in Java.

In the case when implantation of the behavior defined by a function interface already exists (i.e. you have a method somewhere which does what the method declared in the interface is expected to do), you can make use of it with a method reference.

There are four kinds of method references (quote from the Oracles's tutorial referenced above):

  • Reference to a static method ContainingClass::staticMethodName
  • Reference to an instance method of a particular object containingObject::instanceMethodName
  • Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
  • Reference to a constructor ClassName::new

Note that the syntax of method references doesn't require parentheses, neither after a type nor after a method name.

So correct syntax will be Map.Entry::getKey, and that is a reference to an instance method of an arbitrary object of a particular type. I.e. type Map.Entry actually implies an element of the stream (an object, not an interface).

Reminder: Entry is a nested interface that represents a key-value pair, defined in inside a Map interface. Hence, the correct syntax to refer to it Map.Entry (without any parentheses).

And getKey is one of the instance methods declared by the Entry interface.

Recap

The correct syntax to define a Function (which is standard functional interface expected as the first argument by the Collectors.toMap() with a method reference is:

Map.Entry::getKey

Where the first part before the double colon is the type of the object that will be passed to a function. And second part is a method name.

Parentheses are never used after a type in Java (don't confuse the reference to a type with a constructor invocation). And after a method name parentheses are dropped because that how the language was designed, and I guess because method references are intended to be concise and expressive.

风铃鹿 2025-01-22 05:39:41

您可以使用 Map.Entry::getKeyMap.Entry::getValue 但错误是由其他原因引起的。 Map.Entry::getValueInteger::sum 返回不同的类型。

toMap 方法的定义解释了原因:

public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(
    Function<? super T, ? extends K> keyMapper, 
    Function<? super T, ? extends U> valueMapper, 
    BinaryOperator<U> mergeFunction, 
    Supplier<M> mapFactory);

在定义中你可以看到:

  • valueMapper has ? extends U type
  • mergeFunction has U type

将firstMap和secondMap的类型从Map更改为Map,没有错误。这是因为 Map.Entry::getValue 返回的类型从 String 更改为 Integer

这里是最终结果:

public Map<String, Integer> mergeAndAddValues(Map<String, Integer> firstMap, Map<String, Integer> secondMap) {
    return Stream.of(firstMap, secondMap)
                 .flatMap(map -> map.entrySet().stream())
                 .collect(Collectors.toMap(
                        Map.Entry::getKey, Map.Entry::getValue, Integer::sum, HashMap::new));
    }
}

You can use Map.Entry::getKey and Map.Entry::getValue but the error is caused by something else. Map.Entry::getValue and Integer::sum return different types.

The definition of toMap method explains why:

public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(
    Function<? super T, ? extends K> keyMapper, 
    Function<? super T, ? extends U> valueMapper, 
    BinaryOperator<U> mergeFunction, 
    Supplier<M> mapFactory);

In the definition you can see that:

  • valueMapper has ? extends U type
  • mergeFunction has U type

Changing the type of firstMap and secondMap from Map<String, String> to Map<String, Integer>, there are no errors. This is because the type returned by Map.Entry::getValue changes from String to Integer.

Here the final result:

public Map<String, Integer> mergeAndAddValues(Map<String, Integer> firstMap, Map<String, Integer> secondMap) {
    return Stream.of(firstMap, secondMap)
                 .flatMap(map -> map.entrySet().stream())
                 .collect(Collectors.toMap(
                        Map.Entry::getKey, Map.Entry::getValue, Integer::sum, HashMap::new));
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文