这里写目录标题
一、使用规则
在实际项目中我们经常会有 List 转 Map 操作,在过去(JAVA8以前)我们可能使用的是 for 循环遍历的方式,这种方式就不做过多赘述。这里主要讲解使用 Collectors.toMap 方式及使用过程需注意的地方,避免踩坑
二、源码及方法
三个重载的方法如下:
//方法一:2个参数
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
//方法二:3个参数
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
//方法三:4个参数
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator
= (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}
参数含义分别是:
- keyMapper:Key 的映射函数。
- valueMapper:Value 的映射函数。
- mergeFunction:当 Key 冲突时,调用的合并方法。
- mapSupplier:Map 构造器,在需要返回特定的 Map 时使用。
三、代码示例
首先我们建一个Dept类,属性分别有id,name,group,然后构造一个list:
public class ListToMapDemo {
public static void main(String[] args) {
List<Dept> list = new ArrayList<>();
list.add(new Dept(1,"部门一","1"));
list.add(new Dept(2,"部门二","2"));
list.add(new Dept(3,"部门三","3"));
list.add(new Dept(4,"部门四","4"));
}
static class Dept{
private Integer id;
private String name;
private String group;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
public Dept(Integer id, String name, String group) {
this.id = id;
this.name = name;
this.group = group;
}
@Override
public String toString() {
return "Dept{" +
"id=" + id +
", name='" + name + '\'' +
", group='" + group + '\'' +
'}';
}
}
}
1.将list转成以id为key的map,value是id对应的Dept对象
Map<Integer, Dept> map = list.stream().collect(Collectors.toMap(Dept::getId, Function.identity()));
System.out.println(map);
运行结果如下:
{1=Dept{id=1, name='部门一', group='1'}, 2=Dept{id=2, name='部门二', group='2'},
3=Dept{id=3, name='部门三', group='3'}, 4=Dept{id=4, name='部门四', group='4'}}
2.假如id存在重复值,则会报错Duplicate key xxx, 解决方案是
只取后一个key及value:
List<Dept> list = new ArrayList<>();
list.add(new Dept(1,"部门一","1"));
list.add(new Dept(2,"部门二","2"));
list.add(new Dept(3,"部门三","3"));
list.add(new Dept(3,"部门四","4"));
Map<Integer, Dept> map = list.stream().collect(Collectors.toMap(Dept::getId, Function.identity(),(oldValue, newValue) -> newValue));
System.out.println(map);
运行结果如下:
{1=Dept{id=1, name='部门一', group='1'}, 2=Dept{id=2, name='部门二', group='2'}, 3=Dept{id=3, name='部门四', group='4'}}
只取前一个key及value:
Map<Integer, Dept> map = list.stream().collect(Collectors.toMap(Dept::getId,Function.identity(),(oldValue,newValue) -> oldValue));
System.out.println(map);
运行结果如下:
{1=Dept{id=1, name='部门一', group='1'}, 2=Dept{id=2, name='部门二', group='2'},
3=Dept{id=3, name='部门三', group='3'}}
3.想获得一个id和name对应的Map<Integer, String>
Map<Integer, String> map = list.stream().collect(Collectors.toMap(Dept::getId,Dept::getName));
System.out.println(map);
运行结果如下:
{1=部门一, 2=部门二, 3=部门三, 4=部门四}
注意:name可以为空字符串但不能为null,否则会报空指针,解决方案:
Map<Integer, String> map = list.stream().collect(Collectors.toMap(Dept::getId, e -> e.getName() == null ? "" : e.getName()));
假如存在id重复,两个vaue可以这样映射到同一个id:
Map<Integer, String> map = list.stream().collect(Collectors.toMap(Dept::getId,Dept::getName,(e1, e2)->e1+","+e2));
System.out.println(map);
运行结果如下:
{1=部门一, 2=部门二, 3=部门三,部门四}
4.把Dept集合按照group分组到map中
Map<String, List<Dept>> map = list.stream().collect(Collectors.groupingBy(Dept::getGroup));
System.out.println(map);
运行结果如下:
{1=[Dept{id=1, name='部门一', group='1'}], 2=[Dept{id=2, name='部门二', group='2'}],
3=[Dept{id=3, name='部门三', group='3'}, Dept{id=3, name='部门四', group='3'}]}
5.过滤去重,两个List
Map<String, Dept> map2 = list2.stream().collect(Collectors.toMap(Dept::getId,Function.identity()));
//把List1和List2中id重复的Dept对象的name取出来:
List strings = list1.stream().map(Dept::getId).filter(map2::containsKey).map(map2::get).map(Dept::getName).collect(Collectors.toList());
System.out.println(strings);// 输出 [部门2, 部门3]
四、总结
Collectors.toMap 确实带来方便,但是,与此同时,也需要注意两点(你也可以认为是2个坑):
需考虑是否有key重复情况;
需考虑是否有value为null情况。
以上两点在阿里的Java开发手册中也明确指出: