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

来源:互联网 发布:python 图片相似度 库 编辑:程序博客网 时间:2024/04/28 16:24

前言

今天有一个新需求, 是对一个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/


0 0
原创粉丝点击