问题描述
- 小弟尝试将一段如下json读到一个Map<String, List<String>>中,map
的key为condName,map的value为condValue
- 现在用stream结合Collectors的groupingBy和toList方法,将List<Condition>转成了Map<String, List<Condition>>
- 然后尝试通过mapping方法将List<Condition>映射为List<String>时发现无从下手。。。几次尝试后得到了一个Map<String, List<List<String>>>,多了一层List
- 想去掉这一层List,如果再遍历一遍又感觉不太优雅
- 求助各位有什么思路吗,最好是通过mapping一次得到想要的结果
- 不胜感激!
问题出现的环境背景及自己尝试过哪些方法
相关代码
{
"condition": [
{
"condName": "name1",
"condValue": [
"val11",
"val12"
]
},
{
"condName": "name2",
"condValue": [
"val21"
]
},
{
"condName": "name3",
"condValue": [
"val31",
"val32",
"val33"
]
}
],
"total": 3
}
@Data
public class Request {
private List<Condition> condition;
private Long total;
}
@Data
public class Condition {
private String condName;
private List<String> condValue;
}
转换方法(读取json和转成Request对象的代码省略。。。):
String json = getContent();
try {
List<Condition> conditions = deseriliaze(json).getCondition();
Map<String, List<Condition>> map1 = conditions.stream().collect(Collectors.groupingBy(Condition::getCondName));
Map<String, List<List<String>>> map2 = conditions.stream().collect(Collectors.groupingBy(Condition::getCondName, Collectors.mapping(Condition::getCondValue, Collectors.toList())));
} catch (Exception e) {
e.printStackTrace();
}
你期待的结果是什么?实际看到的错误信息又是什么?
- map1:
{name3=[Condition(condName=name3, condValue=[val31, val32, val33])], name2=[Condition(condName=name2, condValue=[val21])], name1=[Condition(condName=name1, condValue=[val11, val12])]}
- map2:
{name3=[[val31, val32, val33]], name2=[[val21]], name1=[[val11, val12]]}
回答这个问题的话,我们可以先来看看为啥会出现
Map<String, List<List<String>>>
的结果,这要从Collectors.groupingBy
的设计语义来说了,它代表把流的数据按照一定规则进行归类分组,并要求提供同一组的数据怎么进行收集的方法,所以这就是Collectors.groupingBy
两个参数的含义那题主第一个参数写的是
Condition::getCondName
,代表流的Condition
按照其condName
属性进行分组,分组之后,同一组的Condition
如何处理,这里题主用的Collectors.mapping(Condition::getCondValue, Collectors.toList()))
,mapping
代表映射转换,这里有点类似分组,Collectors.mapping
第一个参数把流的Condition
转化为List<String>
,此时流里就是List<String>
,再经过第二个参数Collectors.toList()
,哦豁,当然最后结果就是List<List<String>>
这里题主得不到答案的原因就是
Collectors.mapping
的第二个参数没有写对,我这里想到三种方式第一种:还是用
Collectors.mapping
,类似题主提到的再遍历一遍,哈哈这里
Collectors.mapping
的第二个参数用了Collectors.collectingAndThen
,从名字就看得出来,收集好了之后再做什么事...第一个参数当然还是按照Collectors.toList()
,收集到之后,第二个参数再把List
遍历一次,压平后再组成List
emmm,我也不太喜欢这种,不过只是引出了哈
collectingAndThen
,说不定以后题主可以用到第二种:也还是用
Collectors.mapping
,不过这次第二个参数用Collectors.reducing
这里的
Collectors.reducing
就是数据的聚合,正好也符合当前的场景,当流里的数据由Condition
转化为List<String>
时,我们就是要找到一种合并不同List<String>
的方法,所以这里用到这个聚合方法Collectors.reducing
,第一个参数是起始值,第二个参数代表怎么合并两个list
第二个方法要好一点,不过这里我还想到第三个方法
第三种:不用
Collectors.groupingBy
,而采用Collectors.toMap
第三种方式感觉要比前两种简单点,但是这是巧用了哈
toMap
方法,toMap
方法一般使用在数据能够有一种一对一的关系时才用,大多数的时候我们一般也只使用两个参数的方法,即传入怎么获取map
的key
和怎么获取map
的value
的两个Function
,因为一般情况下,业务上可以保证数据是具有一对一的关系的,如果只是调用两参方法,但是实际使用过程中,确实出现了一对多的情况,那么这时候调用toMap
两参方法是会报错的,因此就有了toMap
这个三参方法,第三个参数代表怎么合并同一个key
的value
值,所以到了这里就跟第二种方法的思路差不多了,合并两个集合即可以上就是我的回答,仅供参考
对了,再加一点,我一般是不太喜欢在流里写过长的lambda表达式的,因为流里是要体现当前流程的,不是烂七八糟的都往里噻,所以第三种方式,最后的集合合并,最好还是写成一个
BinaryOperator
,毕竟现在方法也可以是一种参数或者属性了嘛这样我感觉要顺点。。。