【求助】JAVA8 List<Map<>>中过滤出重复元素问题

问题描述

如下所示 List<Map> 数据结构:

    List<Map<String, Object>> list = new ArrayList<>();
    
    Map<String, Object> map1 = new HashMap<>();
    map1.put("order_no", "123");
    map1.put("quantity", 10);
    map1.put("amount", 100);
    
    Map<String, Object> map2 = new HashMap<>();
    map2.put("order_no", "223");
    map2.put("quantity", 15);
    map2.put("amount", 150);
    
    Map<String, Object> map3 = new HashMap<>();
    map3.put("order_no", "123");
    map3.put("quantity", 5);
    map3.put("amount", 50);
    
    Map<String, Object> map4 = new HashMap<>();
    map4.put("order_no", "124");
    map4.put("quantity", 6);
    map4.put("amount", 60);
    
    Map<String, Object> map5 = new HashMap<>();
    map5.put("order_no", "223");
    map5.put("quantity", 7);
    map5.put("amount", 70);
    
    list.add(map1);
    list.add(map2);
    list.add(map3);
    list.add(map4);
    list.add(map5);

有个需求是判断上述list<Map>中,是否存在有Map.key=order_no,其value重复,并将重复的项取出,如例子所示 最后应该会抓到order_no=123,223,这两笔订单,我目前的写法是:

    //定义一个过渡用的list2  与list一致
    List<Map<String, Object>> list2 = new ArrayList<>();
    list2.addAll(list);
    
    List<Map<String, Object>> collect = list.stream().filter(x->{
        long count = list2.stream().filter(x2->x2.get("order_no").equals(x.get("order_no"))).count();
        if(count>1) {  //判断是否重复
            return true;
        }
        return false;
    }).collect(Collectors.groupingBy(x->x.get("order_no"))).entrySet().stream().map(x->{
        Map<String, Object> tmp = new HashMap<>();
        tmp.put("key_order", x.getKey());
        tmp.put("order_list", x.getValue());
        return tmp;  //分组展示重复的订单数据
    }).collect(Collectors.toList());

目前虽然功能是实现的,但是考虑到订单量有数万笔乃至更多,重新定义了一个一样的过渡用list这样的写法比较粗糙,效能也不高,想请教下大家有没有更简洁高效优雅些的方式 可以实现功能呢?

阅读 11.8k
3 个回答

Java8这个Stream本身提供了根据key进行去重的 .distinct()方法,但是没有提供根据value去重的方法,我们只好自己给他写一个扩展。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author 范闲. created in 2018/12/01 00:01
 */
public class StreamEx {

    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}

测试

list
                .stream()
                .filter(StreamEx.distinctByKey(x -> x.get("order_no")))
                .forEach(x -> {
                    System.out.println(x.toString());
                });

//        {order_no=123, amount=100, quantity=10}
//        {order_no=223, amount=150, quantity=15}
//        {order_no=124, amount=60, quantity=6}

可见上述代码打印了去重后的数据信息。

只需要StreamEx中的

return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;

改为

return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) != null;

就可以满足你的要求。

...你用set检查重复不就好了吗 一遍循环就好了 你这个太吓人

        Set<String> set = new HashSet<>();
        Map<String,List<Map<String,Object>>> valMap = new HashMap<>();
        for(Map<String,Object> item:list){
            String id = item.get("order_no").toString();
            if(set.contains(id)){
                List<Map<String, Object>> l = valMap.computeIfAbsent(id, k -> new ArrayList<>());
                l.add(item);
            }
            set.add(id);
        }

        for(Map.Entry<String,List<Map<String,Object>>> entry:valMap.entrySet()){
            System.out.println(JSON.toJSONString(entry.getValue()));
        }
        // print
        // [{"order_no":"123","amount":50,"quantity":5}]
        // [{"order_no":"223","amount":70,"quantity":7}]

这个简单,list添加元素的同时用一个Map去做索引,key为order_no,value为该元素的map,因为只有一份引用所以空间上不会有太大的问题,同时又可以借助map的o(1)查询能力

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题