使用 Java 枚举的流生成映射

Using stream for Java Enum to generate map

提问人:cnmdestroyer 提问时间:11/10/2023 更新时间:11/10/2023 访问量:62

问:

我有一个枚举类

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)

Java Lambda 枚举 java-stream

评论

0赞 experiment unit 1998X 11/10/2023
这回答了你的问题吗?Java 8 中要映射的对象列表
1赞 experiment unit 1998X 11/10/2023
此外,如果您不显示堆栈跟踪,任何人都很难知道静态与非静态问题是什么。可能你有一个静态变量,你正在一个非静态的方法中使用
1赞 experiment unit 1998X 11/10/2023
显示一些真实的代码,而不是......在流中,如果您希望人们使用流纠正重构方法的问题
1赞 shmosel 11/10/2023
你要传递给什么?我猜这不是一个.请提供一个最小的可重复示例map()Function<MyEnum, Map.Entry<MyEnum, MyClass>>

答:

0赞 Reilas 11/10/2023 #1

请尝试以下操作。

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()));
1赞 Basil Bourque 11/10/2023 #2

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

让我们使示例代码更加具体。而不是 ,我们称之为 。我们使用 .MyEnumCodeLetterMyClassjava.time.DayOfWeek

首先,这个领域似乎只是一个干扰,与你的问题无关。因此,我们将其简化为一个简单的枚举声明。key

public enum CodeLetter { A, B, C; }

我们为您提到的类提供实现。传递一个枚举对象,取回一个对象。helperCodeLetterDayOfWeek

DayOfWeek helper ( final CodeLetter codeLetter ) {
    DayOfWeek dow =
            switch ( codeLetter ) {
                case A -> DayOfWeek.SATURDAY;
                case B -> DayOfWeek.SUNDAY;
                case C -> null;
            };
    return dow;
}

现在,完全实现构建地图的示例方法。

  • 我们将该方法从 to 重命名为 。myMethodbuildMap
  • 为了清楚起见,我们重命名为 。resultmap
  • 我们消除了对这种方法的争论,因为它似乎无关紧要。
  • 没有这样的类或方法,所以我们用一个特定的类来代替它。当您将枚举作为映射键时,请使用高度优化的 EnumMap 类。Maps.newHashMapMap
  • Optional<MyClass>,将与我们的新命名一起使用。但通常最好仅用作返回类型。在我们的例子中,允许映射值为 null。因此,我们放弃了使用简单的 null。Optional<CodeLetter>OptionalEnumMapOptional
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=空}

很好,代码运行成功。

回到问题的主旨。您似乎想重构方法以使用流而不是我们的循环。很公平。让我们创建一个方法,作为替代实现。buildMapforbuildMapByStream

要按流构建我们的映射,首先获取键的值流。然后调用该流。对于该方法,我们传递一个实现。Java 通过该方法提供了一个。我们向该方法传递四个参数:一个用于生成每个映射键的 lambda,一个用于生成每个映射值的 lambda,一个用于解决任何冲突的 lambda,以及一个用于构造我们选择的结果实现的 lambda。CodeLettercollectcollectCollectorCollectors.toMaptoMapMap

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。我们可以通过方法引用来缩写它。helperthis :: 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.toMapNullPointerException

幸运的是,该答案提供了一种替代 .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;
    }
}

评论

0赞 cnmdestroyer 11/11/2023
哇,谢谢你的详细解释,真的很有帮助!