java8新特性之toMap的用法——全网独一无二的通俗易懂的讲解


对于java8的新特性toMap方法,相信有很多人都在工作中用过,接下来就通俗易懂的讲解一下toMap吧

官网介绍

先来看看官网对于toMap方法的解释
toMap有个三个重载的方法,每一个重载方法的详解分别如下

(1)方法1:两个参数

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);
}

(2)方法2:三个参数

public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,Function<? super T, ? extends U> valueMapper,
BinaryOperator mergeFunction)
{
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}

(3)方法3:四个参数

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 mergeFunction,Supplier 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 时使用

补充一个重要的知识点,接下来讲解时会依照这个规则讲解

针对于Map集合,在执行put方法时
map.put(k1,v1);
map.put(k1,v2);
最终集合中保留下来的元素是[k1,v2]
总结:每次往Map集合put添加相同的key值时,后面的key对应的value会覆盖前面的那个key对应的value值。


官网看完了接下来开始进行针对性通俗易懂的解释

两个参数的toMap

(1)针对于两个参数的toMap方法,这里简写为toMap(key,value);
循环执行toMap方法的执行的效果相当于

Map map=new HashMap();
for(循环条件) {
if(map.containsKey(oldKey)){
throw new RuntimeException(“xxxxxx”);
}
map.put(key,value);
}
解析:在执行toMap方法在收集元素时,如果遇到了重复的key时,它无法解决这个冲突,即此时会报错。
即如果在循环执行toMap方法,并且往map集合插入重复的key时,默认会报错,具体原因可参考源码。

实践案例:
定义一个User(name=zs,age=23,idNumer=13245906)对象,它有三个属性,

1> Map<String,Integer> map=userList.stream().collect(Collectors.toMap(u->u.getName,u->getAge));
用于收集user类的name与age属性,并且将以key-value键值对的方式插入到map集合中
.
2>Map<String,User> map=userList.stream().collect(Collectors.toMap(u->u.getName,u->u));
用于收集user类的name与User类的对象,并且将key-value键值对的方式插入到map集合中
.
3>Map<String,User> map=userList.stream().collect(Collectors.toMap(u->u.getName,Function.identity()));
用于收集user类的name与User类的对象,并且将key-value键值对的方式插入到map集合中。

注意:这里的Function.identity()是对传入一个对象,并原封不动的返回的是个简法。
在这里插入图片描述
总结:使用这种toMap方法在收集元素时,如果存在重复的Key值时,在收集的过程中会发生冲突,直接导致的后果就是报错。如果想要解决冲突,请参考第二种方法


三个参数的toMap

(2)针对于三个参数的toMap方法,这里简写为两种方式
方式1:toMap(key,value,(k1,k2)->k1);
方式2:toMap(key,value,(k1,k2)->k2);
这两种方式看上去没多大区别,但是意义上却大相径庭,来看看分解后的效果讲解吧

方式1:循环执行toMap方法的执行的效果相当于

Map map=new HashMap();
for(循环条件) {
if(!map.containsKey(oldKey)){
map.put(key,value);
}
}
总结:方式1中的toMap方法在收集元素时,如果遇到了重复的key时,它会放弃收集后面的那个key对应的元素。(k1,k2)->k1就表示k1==k2相同时,保留前面的k1对应的那个键值对,放弃k2对应的那个键值对。

方式2:循环执行toMap方法的执行的效果相当于

Map map=new HashMap();
for(循环条件) {
map.put(key,value);
}
总结:方式1中的toMap方法在收集元素时,如果遇到了重复的key时,它会放弃收集前面的那个key对应的元素。(k1,k2)->k2就表示k1==k2相同时,保留后面最新收集的k2对应的那个键值对,放弃以前收集过的k1对应的那个键值对。

实践案例:
定义一个User(name=zs,age=23,idNumer=13245906)对象,它有三个属性

方式1
Map<String,Integer> map=userList.stream().collect(Collectors.toMap(u->u.getName,u->u.getAge,(k1,k2)->k1));
用于收集user类的name与age属性,并且将以key-value键值对的方式插入到map集合中.
如果在收集过程中发现重名的name值,则保留原先集合中存在的那个key及其对应的value值。舍弃最新待收集的key及其对应的value
.
方式2
Map<String,Integer> map=userList.stream().collect(Collectors.toMap(u->u.getName,u->u.getAge,(k1,k2)->k2));
用于收集user类的name与age属性,并且将以key-value键值对的方式插入到map集合中.
如果在收集过程中发现重名的name值,则舍弃原先集合中存在的那个key及其对应的value值。保留最新待收集的key及其对应的value


四个参数的toMap

(3)针对于四个参数的toMap方法,这里简写为多种方式
方式1:toMap(key,value,(k1,k2)->k1,HashMap::New);
toMap(key,value,(k1,k2)->k2,HashMap::New);
方式2:toMap(key,value,(k1,k2)->k1,TreeMap::New);
toMap(key,value,(k1,k2)->k2,TreeMap::New);
可以对收集到TreeMap集合中的元素按照相应的排序规则进行排序
方式3:toMap(key,value,(k1,k2)->k1,LinkedHashMap::New);
toMap(key,value,(k1,k2)->k2,LinkedHashMap::New);
可以保证收集到LinkedHashMap集合中的元素依旧保持原来的顺序

实践举例:这三种方式其实没多大区别,我们拿其中一个方式举例

针对方式3的toMap(key,value,(k1,k2)->k2,LinkedHashMap::New); 你可以这样理解。
Map map=new LinkedHashMap();
for(循环条件) {
map.put(key,value);
}
解析:在执行toMap方法在收集元素时,创建一个LinkedHashMap集合,然后使用该集合来收集元素,其余的效果同toMap(key,value,(k1,k2)->k2);一样。

经过刚刚的举例讲解,其实也能大概理解这种方式的含义了,它就是在执行toMap方法时构建一个自定义的Map容器,然后循环执行toMap方法时往集合里收集元素,所以该系列方法同三个参数的toMap方法原理几乎一摸一样。

默认情况下,toMap(key,value)与toMap(key,value,(k1,k2)->k1);方法在收集元素时都是构建的一个HashMap容器,然后使用HashMap容器去存储元素,但是HashMap集合我们都知道它是无序的,且无法针对集合中的元素进行排序。所以如果你在工作中有需要针对集合元素进行排序的需求时请使用四个参数的toMap方法.构建一个LinkedHashMap集合可以保证收集的数据依旧保持原来的顺序

总结
(1)toMap(key,value),创建一个HashMap集合用于收集元素,当在收集时出现了重复的key值,它会直接报错,并且中断收集元素到HashMap集合

(2)toMap(key,value,(k1,k2)->k1)创建一个HashMap集合用于收集元素,当在收集时出现了重复的key值时,它会根据(k1,k2)->k1)或者(k1,k2)->k2)这两种规则解决key值重复的冲突并收集元素到HashMap集合

(3) toMap(key,value,(k1,k2)->k2,LinkedHashMap::New)
创建一个LinkedHashMap集合用于收集元素,当在收集时出现了重复的key值时,它会根据(k1,k2)->k1)或者(k1,k2)->k2)这两种规则解决key值重复的冲突并收集元素到LinkedHashMap集合。这种方式的最大好处是可以自定义一个Map集合用于收集元素。

深入理解

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 mergeFunction,
Supplier mapSupplier)

keyMapper与valueMapper均是一个Function函数,而Function函数的特性是传入一个参数,返回一个参数,于是乎我们可以写
Function<? super T, ? extends K> keyMapper = t->k;
Function<? super T, ? extends U> valueMapper = t->u;
BinaryOperator mergeFunction = (o, n)->o 或者 (o, n)->n
Supplier mapSupplier = ()->m;

调用的时候可以这样写:toMap(keyMapper, valueMapper, mergeFunction, mapSupplier);

@Data
public class Student {
   //学号
    private  String no;  
    //姓名
    private  String name;	
}
@Data
public class Teacher{
    //教师号
    private  String no; 
    //姓名 
    private  String name; 
}

list.add(new Student("1001", "小A"));  
     list.add(new Student("1001", "小B"));//学号重复(下面特殊处理)
     list.add(new Student("1002", "小C"));
     list.add(new Student("1003", "小D"));

   //将list转map 【key为多个属性,value为1个属性】  
Map<String, String> map =
    list.stream().collect(Collectors.toMap( 
        obj -> {
          return obj.getNo() + "_" + obj.getName();
        },
        Student::getName,
        (key1 , key2) -> key1   //(map的键重复不会报错,下面已经处理)
));  

Map<String, Teacher> map = 
    list.stream().collect(Collectors.toMap(
        Student::getNo, 
        stu -> {
            Teacher teacher = new Teacher();
            teacher.setNo(stu.getNo());
            teacher.setName(stu.getName());
            return teacher;
        },  
        (key1 , key2) -> key1,LinkedList::New
));
  • 11
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值