zoukankan      html  css  js  c++  java
  • [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/

  • 相关阅读:
    PAT (Advanced Level) 1010. Radix (25)
    PAT (Advanced Level) 1009. Product of Polynomials (25)
    PAT (Advanced Level) 1008. Elevator (20)
    PAT (Advanced Level) 1007. Maximum Subsequence Sum (25)
    PAT (Advanced Level) 1006. Sign In and Sign Out (25)
    PAT (Advanced Level) 1005. Spell It Right (20)
    PAT (Advanced Level) 1004. Counting Leaves (30)
    PAT (Advanced Level) 1001. A+B Format (20)
    PAT (Advanced Level) 1002. A+B for Polynomials (25)
    PAT (Advanced Level) 1003. Emergency (25)
  • 原文地址:https://www.cnblogs.com/wang-meng/p/6139067.html
Copyright © 2011-2022 走看看