Java 中的成对地图列表

List to Map of Pairs in Java

假设我的查询结果是这样的

MyClass(name=AAA, action=action1, price=5),
MyClass(name=BBB, action=action13, price=7),
MyClass(name=AAA, action=action31, price=2)

并想创建一个按 name 分组的地图,其中值将是 actionprice 上的一组 (k,v),如下所示

(AAA=((action1, 5),(action31, 2)), BBB=(action13, 7))

所以一直在尝试如下,但我收到错误 -> 在尝试使用 Map.Entry

时出现“无法从静态内容引用非静态方法”
Stream<Object> trying = results
    .stream()
    .flatMap(it -> {
          Map<String,String> innerMap = new HashMap<>();
          innerMap.put(it.getAction(),it.getPrice());
          Map<String,Map<String,String>> upperMap = new HashMap<>();
          upperMap.put(it.getName(), innerMap);
          return upperMap.entrySet().stream();
        }
    ) 
    .collect(Collectors.groupingBy(Map.Entry::getKey,
        Collectors.mapping(Map.Entry::getValue,
            Collectors.toList())));

您需要以完全糟糕的方式处理它们。这是一种简洁易读的方式:-



public class Builder {

    public static void main(String[] args) throws IOException {
        List<MyClass> list = Arrays.asList(new MyClass("AAA", "action1", 5)
                , new MyClass("BBB", "action13", 7), new MyClass("AAA", "action31", 2));

        Map<String, List<Pairs>> listList = new HashMap<String, List<Pairs>>();

        list.stream().map(l -> {
            List<Pairs> pairs = listList.getOrDefault(l.name, new ArrayList<>());

            pairs.add(new Pairs(l.action, l.price));
            listList.put(l.name, pairs);

            return pairs;
        }).collect(Collectors.toList());

        System.out.println(listList);
//        {AAA=[Pairs{action='action1', price=5}, Pairs{action='action31', price=2}], BBB=[Pairs{action='action13', price=7}]}

    }

}

class Pairs {
  String action;
  int price;

    public Pairs(String action, int price) {
        this.action = action;
        this.price = price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Pairs{");
        sb.append("action='").append(action).append('\'');
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }
}

class MyClass {
    String name;
    String action;
    int price = 5;

    public MyClass(String name, String action, int price) {
        this.name = name;
        this.action = action;
        this.price = price;
    }
}


您可以提供 CollectorgroupingBy 来生成内部地图: (假设静态导入 Collectors.groupingByCollectors.toMap

results.stream()
       .collect(groupingBy(MyClass::getName,
                           toMap(MyClass::getAction,MyClass::getPrice)));       

我的建议是尝试使用 Map<String, List<MyClass>> 类型的地图 这样它会更易于管理。

这是如何编码的想法。

import java.util.*;

public class MyClass {
    String name;
    String action;
    int price;

    public MyClass(String name, String action, int price) {
        this.name = name;
        this.action = action;
        this.price = price;
    }

    @Override
    public String toString() {
        return  "{" + name + ", " + action + ", " + price +"}";
    }

    public static void main(String[] args) {
        Map<String, List<MyClass>> map = new HashMap<>();
        
        MyClass[] objs = {  
                            new MyClass("AAA", "action1", 5),
                            new MyClass("BBB", "action2", 7),
                            new MyClass("AAA", "action3", 2)
                        };

        for(MyClass myClass: objs) {
            if(!map.containsKey(myClass.name)) {
                map.put(myClass.name, new ArrayList<MyClass>());
            }
            map.get(myClass.name).add(myClass);
        }
        
        System.out.println(map);
    }
}

为什么? Map<String, Map<String, String>> 是打字灾难。 Java 是名义上的;如果您使用它擅长的东西,该语言会更好地工作,但事实并非如此。

您的问题描述不明确。想象以下输入:

MyClass(name=AAA, action=action1, price=5),
MyClass(name=BBB, action=action13, price=7),
MyClass(name=AAA, action=action1, price=2)

输出应该是什么?

  • 异常
  • (AAA=((action1, 2)), BBB=(action13, 7))
  • (AAA=((action1, 5)), BBB=(action13, 7))
  • (AAA=((action1, [2, 5])), BBB=(action13, [7]))

最后一个有点明智,因为其他的,尤其是#2 和#3,是完全武断的。即使您想要其中之一,也希望它能帮助您意识到要实现这一目标并非易事、可理解的一系列功能操作,而且您很可能根本不想使用流 - 因为不使用它们会导致代码更具可读性。

无论如何,签名与你写的不符;我假设你搞砸了(你似乎已经将 price 变体的类型设置为 String 这看起来有点疯狂。在这段代码中我将它表示为 Integer,代表原子单位(欧分、聪等)。

如果你真的想要这个(我非常怀疑你真的想要!),那么让我们分解一下步骤:

  • 您首先要分组,给您名称 (AAA) 作为组键,然后是 MyClass 个实例的列表。
  • 然后您希望再次 分组,将具有相同操作名称的所有 MyClass 实例分组在一起,以便以 [2, 5] 整数列表结束。
  • 您还想将 MyClass 实例映射到此过程中某处的价格。

仔细阅读流内容的 API,具体来说,Collectors,给我们你想要的电话:

groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)

Returns a Collector implementing a cascaded "group by" operation on input elements of type T, grouping elements according to a classification function, and then performing a reduction operation on the values associated with a given key using the specified downstream Collector.

让我们开始吧,假设您想要第 4 个选项:

@lombok.Value public class MyClass {
  String name, action;
  int price;
}

List<MyClass> results = List.of(
  new MyClass("AAA", "action1", 5),
  new MyClass("BBB", "action2", 4),
  new MyClass("AAA", "action1", 3),
  new MyClass("AAA", "action3", 2));

Map<String, Map<String, List<MyClass>>> answer =
  results.stream()
  .collect(Collectors.groupingBy(MyClass::getName,
    Collectors.groupingBy(MyClass::getAction)));

System.out.println(answer);

这是很好的第一步,但您只需要一个整数列表,而不是 MyClass 实例列表。我们也可以做到:

Map<String, Map<String, List<Integer>>> answer =
  results.stream()
  .collect(Collectors.groupingBy(MyClass::getName,
    Collectors.groupingBy(MyClass::getAction,
      Collectors.collectingAndThen(Collectors.toList(),
     listOfMc -> listOfMc.stream().map(MyClass::getPrice).collect(Collectors.toList())
  ))));

这让你:

{AAA={action1=[5, 3], action3=[2]}, BBB={action2=[4]}}

我想正是你想要的。

但是,然后回顾该代码并意识到在某个地方,不知何故,您做出了一些错误的选择 :P - 这与易于阅读的代码相去甚远!