将Map<?, Map<?, ?>>转换为对象列表

4

我有如下集合:

Map<String, Map<SomeEnum, Long>> map = ...

以下是样例数据:

{"Foo": {
    SomeEnum.BAR1: 1,
    SomeEnum.BAR2: 2,
    SomeEnum.BAR3: 3
  },
 "two": {...}

既然我已经知道所有枚举值,我想将其转换为POJO列表。该对象的定义如下:

class SomeClass {
  String name;
  long bar1Value;
  long bar2Value;
  long bar3Value;
}

我尝试了不同的解决方案,例如使用嵌套映射:

map.entrySet().stream()
.map(e -> e.getValue().entrySet().stream().map(innerEntry -> {
  long bar1 = 0;
  long bar2 = 0;
  long bar3 = 0;

  if(innerEntry.getKey().equals(SomeEnum.BAR1)) bar1 = innerEntry.getValue();
  if(innerEntry.getKey().equals(SomeEnum.BAR2)) bar2 = innerEntry.getValue();
  if(innerEntry.getKey().equals(SomeEnum.BAR3)) bar3 = innerEntry.getValue();

  return new SomeClass(e.getKey(), bar1, bar2, bar3);
}).collect(toList()).collect(toList());

不幸的是,我得到的是 List<List<SomeClass>>

是否有其他更优雅的方式来处理这个问题?


您正在比较 StringEnum。这样做是行不通的。您可以尝试使用枚举的字符串表示进行比较。 - smac89
2
也许你需要使用flatMap()方法? - Abra
你说得对。我漏掉了那个。你应该尝试使用flatMap而不是建议中的map - smac89
1
@WJS 不好意思,样本数据是以JSON格式提供的。正确版本是签名。 - Forin
@WJS 在帖子中修复了这个问题。 - Forin
显示剩余3条评论
5个回答

4

这个对你有用吗?

它需要目标类的构造函数,但只是获取值并创建实例。然后将它们收集到列表中。

        List<SomeClass> list = map.entrySet().stream()
                .map(e -> new SomeClass(e.getKey(),
                        e.getValue().get(SomeEnum.BAR1),
                        e.getValue().get(SomeEnum.BAR2),
                        e.getValue().get(SomeEnum.BAR3)))
                .collect(Collectors.toList());

有构造函数的类


class SomeClass {
    String name;
    long bar1Value;
    long bar2Value;
    long bar3Value;

    public SomeClass(String name, long bar1Value, long bar2Value,
        long bar3Value) {
        this.name = name;
        this.bar1Value = bar1Value;
        this.bar2Value = bar2Value;
        this.bar3Value = bar3Value;
    }       
}

e.getValue().getOrDefault(SomeEnum.BAR1,0L) 至少可以编译通过。 - Naman
我在编辑时决定不这样做,因为原帖从未暗示任何枚举值缺失。而且,一旦我看到你已经这样做了,再这样做就不合适了,所以我把我的删除了。 - WJS
这整个事情让我感到困惑,因为OP的示例显示他内部Map中的一个String作为键。 - WJS
  1. 在 OP 的代码中选择不设置默认值容易出现 NullPointerException。
  2. 枚举在 JSON 中的表示是其名称,因此除了名称“大多数情况下”解析为枚举值本身之外,没有太多问题。
- Naman

3
您可以通过一个工具,从内部映射中创建SomeClass,简化整个任务:
private SomeClass createSomeClass(String name, Map<SomeEnum, Long> innerMap) {
    return SomeClass.builder()
            .name(name)
            .bar1Value(innerMap.getOrDefault(SomeEnum.BAR1, 0L))
            .bar2Value(innerMap.getOrDefault(SomeEnum.BAR2, 0L))
            .bar3Value(innerMap.getOrDefault(SomeEnum.BAR3, 0L))
            .build();
}

然后你的映射将会非常简单 --

List<SomeClass> someClassList = map.entrySet().stream()
        .map(e -> createSomeClass(e.getKey(), e.getValue()))
        .collect(Collectors.toList());

1
map 改为 flatMap 并删除第一个 toList()
List<SomeClass> ss = map.entrySet().stream()
                .flatMap(e -> e.getValue().entrySet().stream().map(innerEntry -> {
                    long bar1 = 0;
                    long bar2 = 0;
                    long bar3 = 0;

                    if(innerEntry.getKey().equals(SomeEnum.BAR1)) bar1 = innerEntry.getValue();
                    if(innerEntry.getKey().equals(SomeEnum.BAR2)) bar2 = innerEntry.getValue();
                    if(innerEntry.getKey().equals(SomeEnum.BAR3)) bar3 = innerEntry.getValue();

                    return new SomeClass(e.getKey(), bar1, bar2, bar3);
                })).collect(Collectors.toList());

2
谢谢,它将地图压平了,但仍然存在一个问题——新列表包含每个正数 if 的新对象。从我的示例代码中可以看出,名为“Foo”的对象将被创建三次,因为有三个值。 - Forin

1

您需要这样做:

首先添加一个方法来为每个枚举值设置特定的长整型,类似于以下内容:

class SomeClass {

    String name;
    long bar1Value;
    long bar2Value;
    long bar3Value;

//获取器和设置器
    public void setByEnum(SomeEnum t, Long value) {
        switch (t) {
            case BAR1:
                this.setBar1Value(value);
                break;
            case BAR2:
                this.setBar2Value(value);
                break;
            case BAR3:
                this.setBar3Value(value);
                break;
        }
    }
}

然后你需要循环遍历这个地图,类似这样:
 List<SomeClass> list = new ArrayList<>();
    map.forEach((name,values)->{
        SomeClass s = new SomeClass();
        s.setName(name);
        values.forEach((t,value)->{
            s.setByEnum(t, value);
        });
        list.add(s);
    });

0

这样怎么样:

ArrayList<Integer> keyList = new ArrayList<Integer>(map.keySet());
for(int i=0; i<keyList.size(); i++){
    Map innermap = map.get(keyList.get(i));
    SomeClass obj = new SomeClass(keyList.get(i),innermap.get('bar1'),innermap.get('bar2'),innermap.get('bar3');
}

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