提问人:cnmdestroyer 提问时间:11/10/2023 更新时间:11/10/2023 访问量:62
使用 Java 枚举的流生成映射
Using stream for Java Enum to generate map
问:
我有一个枚举类
public enum MyEnum {
A("A"),
B("B");
private final String key;
}
我想生成一个这样的地图:
private Map<MyEnum, MyClass> myMethod(String input) {
Map<MyEnum, MyClass> result = Maps.newHashMap();
for (MyEnum enum : MyEnum.values()) {
Optional<MyClass> value = helper(enum, input);
value.ifPresent(val -> result.put(enum, val));
}
return result;
}
我是 lambda/stream 的新手,我们可以将其重构为类似的东西吗?
private Map<MyEnum, MyClass> myMethod(String input) {
return MyEnum.values().stream().map(...).collect(Collector.toMap(...));
}
我尝试使用,但最后当我使用它时,它有一些静态与非静态问题Arrays.stream()...
collect(Collector.toMap(Map.Entry::getKey, Map.Entry::getValue)
答:
请尝试以下操作。
Map<MyEnum, MyClass> result
= Stream.of(MyEnum.values())
.map(x -> Map.entry(x, helper(x, input)))
.filter(x -> x.getValue().isPresent())
.collect(Collectors.toMap(Map.Entry::getKey, x -> x.getValue().get()));
tl;博士
public enum CodeLetter { A, B, C; }
Map < CodeLetter, DayOfWeek > map =
Stream
.of ( CodeLetter.values ( ) )
.collect (
( ) -> new EnumMap <> ( CodeLetter.class ) ,
( Map < CodeLetter, DayOfWeek > m , CodeLetter v ) -> m.put ( v , this.helper ( v ) ) ,
Map :: putAll
);
详
让我们使示例代码更加具体。而不是 ,我们称之为 。我们使用 .MyEnum
CodeLetter
MyClass
java.time.DayOfWeek
首先,这个领域似乎只是一个干扰,与你的问题无关。因此,我们将其简化为一个简单的枚举声明。key
public enum CodeLetter { A, B, C; }
我们为您提到的类提供实现。传递一个枚举对象,取回一个对象。helper
CodeLetter
DayOfWeek
DayOfWeek helper ( final CodeLetter codeLetter ) {
DayOfWeek dow =
switch ( codeLetter ) {
case A -> DayOfWeek.SATURDAY;
case B -> DayOfWeek.SUNDAY;
case C -> null;
};
return dow;
}
现在,完全实现构建地图的示例方法。
- 我们将该方法从 to 重命名为 。
myMethod
buildMap
- 为了清楚起见,我们重命名为 。
result
map
- 我们消除了对这种方法的争论,因为它似乎无关紧要。
- 没有这样的类或方法,所以我们用一个特定的类来代替它。当您将枚举作为映射键时,请使用高度优化的
EnumMap
类。Maps.newHashMap
Map
Optional<MyClass>
,将与我们的新命名一起使用。但通常最好仅用作返回类型。在我们的例子中,允许映射值为 null。因此,我们放弃了使用简单的 null。Optional<CodeLetter>
Optional
EnumMap
Optional
private Map < CodeLetter, DayOfWeek > buildMap ( ) {
Map < CodeLetter, DayOfWeek > map = new EnumMap <> ( CodeLetter.class );
for ( CodeLetter codeLetter : CodeLetter.values ( ) ) {
DayOfWeek dow = this.helper ( codeLetter );
map.put ( codeLetter , dow );
}
return map;
}
执行此代码。
EnumMapExample app = new EnumMapExample ( );
Map < CodeLetter, DayOfWeek > map = app.buildMap ( );
System.out.println ( "map = " + map );
运行时:
地图 = {A=星期六, B=星期日, C=空}
很好,代码运行成功。
回到问题的主旨。您似乎想重构方法以使用流而不是我们的循环。很公平。让我们创建一个方法,作为替代实现。buildMap
for
buildMapByStream
要按流构建我们的映射,首先获取键的值流。然后调用该流。对于该方法,我们传递一个实现。Java 通过该方法提供了一个。我们向该方法传递四个参数:一个用于生成每个映射键的 lambda,一个用于生成每个映射值的 lambda,一个用于解决任何冲突的 lambda,以及一个用于构造我们选择的结果实现的 lambda。CodeLetter
collect
collect
Collector
Collectors.toMap
toMap
Map
private Map < CodeLetter, DayOfWeek > buildMapByStream ( ) {
Map < CodeLetter, DayOfWeek > map =
Stream
.of ( CodeLetter.values ( ) )
.collect (
Collectors.toMap (
( CodeLetter codeLetter ) -> codeLetter ,
( CodeLetter codeLetter ) -> this.helper ( codeLetter ) ,
( DayOfWeek dow1 , DayOfWeek dow2 ) -> dow1 ,
( ) -> new EnumMap <> ( CodeLetter.class )
)
);
return map;
}
我们可以简化前两个参数。
- 第一个参数是一个仅返回传递对象的 lambda。Java 提供了一个提供相同功能的 Function 实现:
Function.identity
。 - 第二个参数是调用我们方法的 lambda。我们可以通过方法引用来缩写它。
helper
this :: helper
在这些更改之后,我们的代码如下所示:
private Map < CodeLetter, DayOfWeek > buildMapByStream ( ) {
Map < CodeLetter, DayOfWeek > map =
Stream
.of ( CodeLetter.values ( ) )
.collect (
Collectors.toMap (
Function.identity ( ) ,
this :: helper ,
( DayOfWeek dow1 , DayOfWeek dow2 ) -> dow1 ,
( ) -> new EnumMap <> ( CodeLetter.class )
)
);
return map;
}
我们应该能够考虑完成的工作。不幸的是,Java 8 到 21 有一个已知的错误,即当生成的映射值为 null 时会抛出 a。请参阅此问题,以及 kajacx 的此答案。Collectors.toMap
NullPointerException
幸运的是,该答案提供了一种替代 .Collectors.toMap
private Map < CodeLetter, DayOfWeek > buildMapByStream ( ) {
Map < CodeLetter, DayOfWeek > map =
Stream
.of ( CodeLetter.values ( ) )
.collect (
( ) -> new EnumMap <> ( CodeLetter.class ) ,
( Map < CodeLetter, DayOfWeek > m , CodeLetter v ) -> m.put ( v , this.helper ( v ) ) ,
Map :: putAll
);
return map;
}
在这一点上,我们可以练习我们的方法。buildMapByStream
EnumMapExample app = new EnumMapExample ( );
Map < CodeLetter, DayOfWeek > map = app.buildMapByStream ( );
System.out.println ( "map = " + map );
地图 = {A=星期六, B=星期日, C=空}
这行得通,工作完成了。
这是完整的代码,以方便您复制粘贴。
package work.basil.example.enums;
import java.time.DayOfWeek;
import java.util.EnumMap;
import java.util.Map;
import java.util.stream.Stream;
public class EnumMapExample {
public static void main ( String[] args ) {
EnumMapExample app = new EnumMapExample ( );
Map < CodeLetter, DayOfWeek > map = app.buildMapByStream ( );
System.out.println ( "map = " + map );
}
private Map < CodeLetter, DayOfWeek > buildMap ( ) {
Map < CodeLetter, DayOfWeek > map = new EnumMap <> ( CodeLetter.class );
for ( CodeLetter codeLetter : CodeLetter.values ( ) ) {
DayOfWeek dow = this.helper ( codeLetter );
map.put ( codeLetter , dow );
}
return map;
}
private Map < CodeLetter, DayOfWeek > buildMapByStream ( ) {
Map < CodeLetter, DayOfWeek > map =
Stream
.of ( CodeLetter.values ( ) )
.collect (
( ) -> new EnumMap <> ( CodeLetter.class ) ,
( Map < CodeLetter, DayOfWeek > m , CodeLetter v ) -> m.put ( v , this.helper ( v ) ) ,
Map :: putAll
);
return map;
}
DayOfWeek helper ( final CodeLetter codeLetter ) {
DayOfWeek dow =
switch ( codeLetter ) {
case A -> DayOfWeek.SATURDAY;
case B -> DayOfWeek.SUNDAY;
case C -> null;
};
return dow;
}
}
评论
map()
Function<MyEnum, Map.Entry<MyEnum, MyClass>>