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
分组的地图,其中值将是 action
和 price
上的一组 (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;
}
}
您可以提供 Collector
和 groupingBy
来生成内部地图:
(假设静态导入 Collectors.groupingBy
和 Collectors.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 - 这与易于阅读的代码相去甚远!
假设我的查询结果是这样的
MyClass(name=AAA, action=action1, price=5),
MyClass(name=BBB, action=action13, price=7),
MyClass(name=AAA, action=action31, price=2)
并想创建一个按 name
分组的地图,其中值将是 action
和 price
上的一组 (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;
}
}
您可以提供 Collector
和 groupingBy
来生成内部地图:
(假设静态导入 Collectors.groupingBy
和 Collectors.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 - 这与易于阅读的代码相去甚远!