使用Java Enum的Stream生成Map

1qczuiv0  于 2023-11-15  发布在  Java
关注(0)|答案(2)|浏览(112)

我有一个枚举类

public enum MyEnum {
    A("A"),
    B("B");

    private final String key;
}

字符串
我想生成一个这样的Map:

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)时,它有一些静态与非静态的问题

bkhjykvo

bkhjykvo1#

tl;干

public enum CodeLetter { A, B, C; }

个字符

详情

让我们让你的示例代码更具体。我们称之为CodeLetter而不是MyEnum。我们使用java.time.DayOfWeek而不是MyClass
首先,似乎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;
}


现在完全实现构建Map的示例方法。

  • 我们将该方法从myMethod重命名为buildMap
  • 为了清晰起见,我们将result重命名为map
  • 我们排除了对该方法的论证,因为它似乎无关紧要。
  • 没有这样的类或方法Maps.newHashMap,所以我们用一个实现Map的特定类来代替它。当你有一个枚举作为你的Map键时,使用高度优化的EnumMap类。
  • Optional<MyClass>,在我们的新命名下将是Optional<CodeLetter>。但是Optional通常最好只用作返回类型。在我们这里的例子中,EnumMap允许map值为null。所以我们放弃了Optional,而是使用简单的null。
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 );


运行时:
map = {A=SATURDAY,B=SUNDAY,C=null}
很好,代码成功运行。
回到你的问题的核心。你似乎想重构buildMap方法以使用流而不是我们的for循环。很好。让我们创建一个buildMapByStream方法,作为替代实现。
要通过流来构建map,首先获取我们的key CodeLetter的值的流。然后在该流上调用collect。我们向collect方法传递一个Collector实现。Java通过Collectors.toMap方法提供一个。我们向toMap方法传递四个参数:一个lambda用于生成每个Map键,一个lambda用于生成每个Map值,一个lambda用于解决任何冲突,以及一个lambda用于构造我们选择的结果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方法。我们可以用method referencethis :: 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在Collectors.toMap中有a known bug,当生成的map值为null时,会抛出NullPointerException。参见kajacx的this Questionthis Answer
幸运的是,Answer提供了一个替代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 );

map = {A=SATURDAY,B=SUNDAY,C=null}
很好,工作完成了。
下面是完整的代码,以方便您的复制粘贴。

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;
    }
}
2g32fytz

2g32fytz2#

尝试以下操作。

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

字符串

相关问题