JAVA集合05_Collection.toMap()应用三个重载方法解决重复key问题

Posted 所得皆惊喜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA集合05_Collection.toMap()应用三个重载方法解决重复key问题相关的知识,希望对你有一定的参考价值。

文章目录

①. Collectors.toMap介绍

  • ①. 在项目开发过程中,我们会将List转为Map拿到特定的数据进行处理

  • ②. 一般来说,转为Map的key尽量保持唯一

public class CollectionToMapDemo 
    public static void main(String[] args) 
        House house = new House(1,1,"aa","北京海淀");
        House house1 = new House(2,2,"bb","湖北武汉");
        House house2 = new House(3,3,"cc","浙江杭州");
        List<House> houses = Lists.newArrayList();
        houses.add(house);
        houses.add(house1);
        houses.add(house2);
        //在实际项目中我们经常会用到 List 转 Map 操作 ->过去是for循环的操作,现在可以学习如下的方法Collectors.toMap
        /**
         * 我们收集一下集合中每个对象的两个单独的属性
         */
        Map<String, String> mapHouse = houses.stream().collect(Collectors.toMap(House::getHouseName, House::getAddress));
        System.out.println(mapHouse);
        //cc=浙江杭州, bb=湖北武汉, aa=北京海淀

        /**
         * 前后的属性的数据类型要对应  一般时间业务中收集带有唯一表示的业务数据
         */
        Map<Integer, String> map = houses.stream().collect(Collectors.toMap(House::getOwnerId, House::getHouseName));
        System.out.println(map);
        //1=aa, 2=bb, 3=cc

        /**
         * 收集一下属性和对象本身
         */
        Map<Integer, House> houseMap = houses.stream().collect(Collectors.toMap(House::getOwnerId, o -> o));
        Map<Integer, House> houseMap1 = houses.stream().collect(Collectors.toMap(House::getOwnerId,  Function.identity()));
        System.out.println(houseMap);
        /**
         1=House(id=1, ownerId=1, houseName=aa, address=北京海淀),
         2=House(id=2, ownerId=2, houseName=bb, address=湖北武汉),
         3=House(id=3, ownerId=3, houseName=cc, address=浙江杭州)
         */

        //业务场景:一般会根据具体的键值 取具体的对象
        System.out.println(houseMap.get(1));
        //House(id=1, ownerId=1, houseName=aa, address=北京海淀)

        //此处的效果同houseMap
        System.out.println(houseMap1);
        /**
         1=House(id=1, ownerId=1, houseName=aa, address=北京海淀),
         2=House(id=2, ownerId=2, houseName=bb, address=湖北武汉),
         3=House(id=3, ownerId=3, houseName=cc, address=浙江杭州)
         */
    

@Data
@AllArgsConstructor
class House 
    private Integer id;        //id
    private Integer ownerId;   //家主编号
    private String houseName;  //家庭名称
    private String address;    //家庭地址

②. Collectors.toMap有三个重载方法

  • ①. Collectors.toMap有三个重载方法:
  1. keyMapper:Key 的映射函数
  2. valueMapper:Value 的映射函数
  3. mergeFunction:当 Key 冲突时,调用的合并方法
  4. mapSupplier:Map 构造器,在需要返回特定的 Map 时使用
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
        BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
        BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);
  • ②. 第一个默认生成HashMap:如果key冲突了,会默认抛出异常
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);
    
  • ③. 第二个默认生成也是一个HashMap:如果key冲突了,可以按照指定的要求进行合并或者取其任意一个
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);
    
  • ④. 第三个可以自定义异常方法和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<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);
    
  • ⑤. 如解决toMap()乱序:使用LinkedHashMap
public class CollectionToMapDemo 
    public static void main(String[] args) 
        House house = new House(1,1,"aa","北京海淀");
        House house1 = new House(2,2,"bb","湖北武汉");
        House house2 = new House(3,3,"cc","浙江杭州");
        List<House> houses = Lists.newArrayList();
        houses.add(house);
        houses.add(house2);
        houses.add(house1);

        /**
         * 收集一下属性和对象本身
         */
        Map<Integer, House> houseMap = houses.stream().collect(Collectors.toMap(House::getOwnerId, o -> o));
        System.out.println(houseMap);
        /**
         1=House(id=1, ownerId=1, houseName=aa, address=北京海淀),
         2=House(id=2, ownerId=2, houseName=bb, address=湖北武汉),
         3=House(id=3, ownerId=3, houseName=cc, address=浙江杭州)
         */
        LinkedHashMap<Integer, House> collect = houses.stream().collect(Collectors.toMap(House::getOwnerId, Function.identity(), (v1, v2) -> v1, LinkedHashMap::new));
        //会按照添加顺序进行处理
        System.out.println(collect);
        /**
         1=House(id=1, ownerId=1, houseName=aa, address=北京海淀), 
         3=House(id=3, ownerId=3, houseName=cc, address=浙江杭州), 
         2=House(id=2, ownerId=2, houseName=bb, address=湖北武汉)
         */
    

@Data
@AllArgsConstructor
class House 
    private Integer id;        //id
    private Integer ownerId;   //家主编号
    private String houseName;  //家庭名称
    private String address;    //家庭地址

③. 收集一下属性和对象本身

houses.stream().collect(Collectors.toMap(House::getOwnerId, o -> o));
// 如果key相同了,这种方式会抛出异常
houses.stream().collect(Collectors.toMap(House::getOwnerId,  Function.identity()));
// 如果key相同了,可以按照指定规则处理
houses.stream().collect(Collectors.toMap(House::getOwnerId,  Function.identity(),(v1,v2)->v1));
        List<User>userList=new ArrayList<>();
        userList.add(new User(1,"a"));
        userList.add(new User(2,"b"));
        userList.add(new User(3,"c"));
        userList.add(new User(4,"取前者的值"));
        userList.add(new User(4,"取后者的值"));
        userList.add(new User(5,"e"));
        /**
         如果key中有值重复,取前者(这里打印的是 4:取前者的值)
         1====User(id=1, name=a)
         2====User(id=2, name=b)
         3====User(id=3, name=c)
         4====User(id=4, name=取前者的值)
         5====User(id=5, name=e)
         */
        //Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, k1 -> k1, (K1, K2) -> K1));
        /**
         如果key中有值重复,取后者(这里打印的是 4:取后者的值)
         1====User(id=1, name=a)
         2====User(id=2, name=b)
         3====User(id=3, name=c)
         4====User(id=4, name=取后者的值)
         5====User(id=5, name=e)
         */
        Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, Function.identity(), (K1, K2) -> K2));
        for (Integer key:userMap.keySet())
            System.out.println(key+"===="+userMap.get(key));
        

④. 常见的java.lang.IllegalStateException: Duplicate key问题处理

  • ①. 修改数据,两个一样的key
		House house = new House(1,1,"aa","北京海淀");
		House house1 = new House(2,2,"bb","湖北武汉");
		House house2 = new House(3,3,"cc","浙江杭州");
		House house3 = new House(3,3,"cc","浙江杭州");
		List<House> houses = Lists.newArrayList();
		houses.add(house);
		houses.add(house1);
		houses.add(house2);
		houses.add(house3);
		
		/**
		* 收集一下属性和对象本身
		*/
		Map<Integer, House> houseMap = houses.stream().collect(Collectors.toMap(House::getOwnerId, o -> o));
		System.out.println(houseMap);
  • ②. 线上业务代码出现Duplicate Key的异常,影响了业务逻辑,查看抛出异常部分的代码
Exception in thread "main" java.lang.IllegalStateException: Duplicate key House(id=3, ownerId=3, houseName=cc, address=浙江杭州)
	at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
	at java.util.HashMap.merge(HashMap.java:1253)
	at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
	at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
	at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1374)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
	at com.xiaozhi.collection.CollectionToMapDemo.main(CollectionToMapDemo.java:29)
Process finished with exit code 1
  • ③. 解决办法:出现重复时,取前面value的值,或者取后面放入的value值,则覆盖先前的value值
houses.stream().collect(Collectors.toMap(House::getOwnerId, House::getHousename,(v1,v2)->v2));
houses.stream().collect(Collectors.toMap(House::getOwnerId, House::getHousename,(v1,v2)->v1));
  • ④. 对于结果的操作还有很多处理方法,如拼接等
houses.stream().collect(Collectors.toMap(House::getOwnerId, 05_集合对象注入

Java多线程系列--“JUC集合”05之 ConcurrentSkipListMap

[java初探05]__数组的简单认识及Arrays类的常用方法

阶段1 语言基础+高级_1-3-Java语言高级_05-异常与多线程_第5节 线程池_1_线程池的概念和原理

java集合框架05——ArrayList和LinkedList的区别

集合05_Collections工具类