• [Java Collection]List分组之简单应用.


    前言

    今天有一个新需求, 是对一个List进行分组, 于是便百度到一些可用的代码以及我们项目使用的一些tools, 在这里总结下方便以后查阅.

    一: 需求

    现在我们一个数据库表t_series_value_rate存储的是每个汽车对应的保值率. 其中一个车系id可以对应多条数据.表内容部分截取如下:
    file-list

    其中series_id是车系id, car_year代表这个车的年限. 比如说车系id为1的车1年的保值率为81.5%, 2年的保值率为73.7%.

    那么现在我需要传递过来一个series_id list去查询出相应的数据, 我们是对数据库表t_series_value_rate查询所有做了缓存的, 如果我们对传入的series_id list进行遍历的话势必会很慢. 所以如果能够根据series_id进行分组的话, 那么效率就会高的多.

    二: 代码示例

    对外暴漏的API接口, 方便别的项目调用:

    public List<HashMap<Long, List<SeriesValueRateDTO>>> listSeriesValueRates(List<Long> seriesIds) throws Exception {
        ApiResponse response = httpGet("/api/server/series-value-rates/list-series-value-rates.htm?seriesIds=" + Joiner.on(",").skipNulls().join(seriesIds));
        return JSON.parseObject(response.getJsonObject().get("data").toString(), new TypeReference<List<HashMap<Long, List<SeriesValueRateDTO>>>>(){});
    }
    

    这里我们传入的是一个车系id集合.
    然后继续往下调用:

    public List<SeriesValueRateDTO> listSeriesValueRate(final long seriesId) {
        String key = "listSeriesValueRate" + seriesId;
        return managerService.get(key, new Callable<List<SeriesValueRateDTO>>() {
            @Override
            public List<SeriesValueRateDTO> call() throws Exception {
                Map<Long, List<SeriesValueRateDTO>> valueRateDTOs = listAllSeriesValueRate();
                List<SeriesValueRateDTO> dtos = valueRateDTOs.get(seriesId);
                return dtos;
            }
        }, CommonConstants.ExpiredTime.ONE_DAY);
    }
    

    这里使用了DCache去缓存不同的seriesId对应的数据, 接下来再看看查询所有车系保值率数据(listAllSeriesValueRate()):

    private LoadingCache<String, Map<Long, List<SeriesValueRateDTO>>> cache = CacheBuilder.newBuilder()
                .expireAfterWrite(12,TimeUnit.HOURS)
                    .build(new CacheLoader<String, Map<Long, List<SeriesValueRateDTO>>>() {
        @Override
        public Map<Long, List<SeriesValueRateDTO>> load(String k) {
            List<SeriesValueRateDTO> valueRateDTOs = Lists.newArrayList();
            List<SeriesValueRateEntity> entities = seriesValueRateEntityDao.findAll(SeriesValueRateEntity.Fields.seriesId.notNull());
            for (SeriesValueRateEntity entity : entities) {
                SeriesValueRateDTO dto = new SeriesValueRateDTO();
                dto.setSeriesId(entity.getSeriesId());
                dto.setCarYear(entity.getCarYear());
                dto.setValueRate(entity.getValueRate());
                valueRateDTOs.add(dto);
            }
    
            //按照seriesId进行分组
            Map<Long, List<SeriesValueRateDTO>> map = Maps.newHashMap(); //第17行
            GroupUtils.listGroup2Map(valueRateDTOs, map, SeriesValueRateDTO.class, "getSeriesId");//第18行
    
            return map;
        }
    });
    

    这里使用了GuavaCache去缓存所有的车系保值率数据, 然后这里使用了GroupUtils去进行分组, 分组是按照"getSeriesId"来获取seriesId进行分组. 我们来查看下分组前的数据结构(代码中第17行处查看debug数据):
    file-list
    然后再看看分组后的数据结构(运行完第18行数据结果):
    file-list

    很显然, 数据已经进行了分组, 最后看看我们是如何高效率的通过传入的seriesIds取值的:

    public List<HashMap<Long, List<SeriesValueRateDTO>>> listSeriesValueRates() {
        WebContext context = WebContext.get();
        List<Long> ids =context.getRequiredLongList("seriesIds");
        List<HashMap<Long, List<SeriesValueRateDTO>>> seriesValueRateDTOs = Lists.newArrayList();
        for (long seriesId : ids) {
            HashMap<Long, List<SeriesValueRateDTO>> map = Maps.newHashMap();
            List<SeriesValueRateDTO> dtos = seriesValueRateEntityService.listSeriesValueRate(seriesId);
            map.put(seriesId, dtos);
            seriesValueRateDTOs.add(map);
        }
    
        return seriesValueRateDTOs;
    }
    
    public List<SeriesValueRateDTO> listSeriesValueRate(final long seriesId) {
        String key = "listSeriesValueRate" + seriesId;
        return managerService.get(key, new Callable<List<SeriesValueRateDTO>>() {
            @Override
            public List<SeriesValueRateDTO> call() throws Exception {
                Map<Long, List<SeriesValueRateDTO>> valueRateDTOs = listAllSeriesValueRate();
                List<SeriesValueRateDTO> dtos = valueRateDTOs.get(seriesId);
                return dtos;
            }
        }, CommonConstants.ExpiredTime.ONE_DAY);
    }
    

    这里再放上SeriesValueRateDTO:

    public class SeriesValueRateDTO {
        /**
         * 车系id
         */
        private long seriesId;
        /**
         * 保值率
         */
        private Double valueRate;
        /**
         * 车辆年限
         */
        private int carYear;
    
        public long getSeriesId() {
            return seriesId;
        }
    
        public void setSeriesId(long seriesId) {
            this.seriesId = seriesId;
        }
    
        public Double getValueRate() {
            return valueRate;
        }
    
        public void setValueRate(Double valueRate) {
            this.valueRate = valueRate;
        }
    
        public int getCarYear() {
            return carYear;
        }
    
        public void setCarYear(int carYear) {
            this.carYear = carYear;
        }
    }
    

    三: 分组工具类GroupUtils

    这里直接铺上代码, 其实也很简单, 具体使用规则请参考上面.

    public class GroupUtils {
        private static final Logger LOGGER = LoggerFactory.getLogger(GroupUtils.class);
        /**
         * 分组依赖接口
         */
        public interface GroupBy<T> {
            T groupby(Object obj);
        }
    
        /**
         *
         * @param colls
         * @param gb
         * @return
         */
        public static final <T extends Comparable<T>, D> Map<T, List<D>> group(Collection<D> colls, GroupBy<T> gb) {
            if (colls == null || colls.isEmpty()) {
                LOGGER.info("分组集合不能为空!");
                return null;
            }
            if (gb == null) {
                LOGGER.info("分组依赖接口不能为Null!");
                return null;
            }
            Iterator<D> iter = colls.iterator();
            Map<T, List<D>> map = new HashMap<T, List<D>>();
            while (iter.hasNext()) {
                D d = iter.next();
                T t = gb.groupby(d);
                if (map.containsKey(t)) {
                    map.get(t).add(d);
                } else {
                    List<D> list = new ArrayList<D>();
                    list.add(d);
                    map.put(t, list);
                }
            }
            return map;
        }
        /**
         * 将List<V>按照V的methodName方法返回值(返回值必须为K类型)分组,合入到Map<K, List<V>>中<br>
         * 要保证入参的method必须为V的某一个有返回值的方法,并且该返回值必须为K类型
         *
         * @param list
         *            待分组的列表
         * @param map
         *            存放分组后的map
         * @param clazz
         *            泛型V的类型
         * @param methodName
         *            方法名
         */
        public static <K, V> void listGroup2Map(List<V> list, Map<K, List<V>> map, Class<V> clazz, String methodName) {
            // 入参非法行校验
            if (null == list || null == map || null == clazz) {
                LOGGER.info("CommonUtils.listGroup2Map 入参错误,list:" + list + " ;map:" + map + " ;clazz:" + clazz + " ;methodName:" + methodName);
                return;
            }
    
            // 获取方法
            Method method = getMethodByName(clazz, methodName);
            // 非空判断
            if (null == method) {
                return;
            }
    
            // 正式分组
            listGroup2Map(list, map, method);
        }
        /**
         * 根据类和方法名,获取方法对象
         *
         * @param clazz
         * @param methodName
         * @return
         */
        public static Method getMethodByName(Class<?> clazz, String methodName) {
            Method method = null;
            // 入参不能为空
            if (null == clazz) {
                LOGGER.info("GroupUtils.getMethodByName 入参错误,clazz:" + clazz + " ;methodName:" + methodName);
                return method;
            }
    
            try {
                method = clazz.getDeclaredMethod(methodName);
            } catch (Exception e) {
                LOGGER.info("类获取方法失败!");
            }
    
            return method;
        }
        /**
         * 将List<V>按照V的某个方法返回值(返回值必须为K类型)分组,合入到Map<K, List<V>>中<br>
         * 要保证入参的method必须为V的某一个有返回值的方法,并且该返回值必须为K类型
         *
         * @param list
         *            待分组的列表
         * @param map
         *            存放分组后的map
         * @param method
         *            方法
         */
        @SuppressWarnings("unchecked")
        public static <K, V> void listGroup2Map(List<V> list, Map<K, List<V>> map, Method method) {
            // 入参非法行校验
            if (null == list || null == map || null == method) {
                LOGGER.info("GroupUtils.listGroup2Map 入参错误,list:" + list + " ;map:" + map + " ;method:" + method);
                return;
            }
    
            try {
                // 开始分组
                Object key;
                List<V> listTmp;
                for (V val : list) {
                    key = method.invoke(val);
                    listTmp = map.get(key);
                    if (null == listTmp) {
                        listTmp = new ArrayList<V>();
                        map.put((K) key, listTmp);
                    }
                    listTmp.add(val);
                }
            } catch (Exception e) {
                LOGGER.info("分组失败!");
            }
        }
    }
    

    最后大家可以根据自己的需求来选择改造或使用. 回头发现项目中能学到的东西很多, 记录下来希望以后能够多看看. 2016/12/06 http://www.cnblogs.com/wang-meng/

  • 相关阅读:
    base64编码的字符串与图片相互转换
    超酷3D照片展示效果
    table内容保存到Excel中
    项目管理--PMBOK 读书笔记(3)【项目经理的角色 】
    项目管理--PMBOK 读书笔记(2)【项目运行环境】
    项目管理--PMBOK 读书笔记(1)【引论】
    C# ASP.NET递归循环生成嵌套json结构树
    将XML转换为JSON并强制数组
    Yapi Docker 部署
    Spring Cloud Feign+Hystrix自定义异常处理
  • 原文地址:https://www.cnblogs.com/wang-meng/p/6139067.html
Copyright © 2020-2023  润新知