[Java Collection]List分组之简单应用.
前言
今天有一个新需求, 是对一个List进行分组, 于是便百度到一些可用的代码以及我们项目使用的一些tools, 在这里总结下方便以后查阅.
一: 需求
现在我们一个数据库表t_series_value_rate存储的是每个汽车对应的保值率. 其中一个车系id可以对应多条数据.表内容部分截取如下:
其中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数据):
然后再看看分组后的数据结构(运行完第18行数据结果):
很显然, 数据已经进行了分组, 最后看看我们是如何高效率的通过传入的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/
[Java Collection]List分组之简单应用.的更多相关文章
- java Collection中的排序问题
java Collection中的排序问题 这里讨论list.set.map的排序,包括按照map的value进行排序. 1)list排序 list排序可以直接采用Collections的sort方法 ...
- JAVA 集合 List 分组的两种方法
CSDN日报20170219--<程序员的沟通之痛> [技术直播]揭开人工智能神秘的面纱 程序员1月书讯 云端应用征文大赛,秀绝招,赢无人机! JAVA 集合 List 分组的两种方法 2 ...
- Java Collection开发技巧
Java Collection(集合) 集合中的一些技巧: 通过Collections类的静态方法,可以对集合进行一些操作 1 java.util.List<Integer> number ...
- java基础学习02(简单的java程序)
简单的java程序 一.完成的目标 1. 理解java程序的基本组成 2. 如何对程序代码进行注释 3. java标识符的命名规则 4. 了解java中的关键字 5. 使用java定义变量或声明变量 ...
- Java设计模式(一) 简单工厂模式不简单
摘要:本文介绍了简单工厂模式的概念,优缺点,实现方式,以及结合Annotation和反射的改良方案(让简单工厂模式不简单).同时介绍了简单工厂模式(未)遵循的OOP原则.最后给出了简单工厂模式在JDB ...
- java中Color类的简单总结
java中Color类的简单总结 1.颜色的常识 任何颜色都是由三原色组成(RGB),JAVA中支持224为彩色,即红绿蓝分量取值 介于0-255之间(8位表示) 2.Color类中的常量 publi ...
- Java Collection Framework概述
文章出自:听云博客 Collection概述 Java collection是java提供的工具包,包含了常用的数据结构:集合.链表.队列.栈.数组.映射等. Java集合主要可以划分为4个部分:Li ...
- Java Collection好文章
Java Collection好文章 http://my.oschina.net/xiaomaoandhong/blog/78394
- spring 第一篇(1-1):让java开发变得更简单(下)
切面(aspects)应用 DI能够让你的软件组件间保持松耦合,而面向切面编程(AOP)能够让你捕获到在整个应用中可重用的组件功能.在软件系统中,AOP通常被定义为提升关注点分离的一个技术.系统由很多 ...
随机推荐
- NodeJs之child_process
一.child_process child_process是NodeJs的重要模块.帮助我们创建多进程任务,更好的利用了计算机的多核性能. 当然也支持线程间的通信. 二.child_process的几 ...
- Spark踩坑记——Spark Streaming+Kafka
[TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...
- 支持 .NET Core 的 Memcached 客户端 EnyimMemcachedCore
1. 介绍 EnyimMemcachedCore 是一个支持 .NET Core 的 Memcached 客户端,是从 EnyimMemcached 迁移至 .NET Core的,源代码托管在 Git ...
- 我们是怎么做Code Review的
前几天看了<Code Review 程序员的寄望与哀伤>,想到我们团队开展Code Review也有2年了,结果还算比较满意,有些经验应该可以和大家一起分享.探讨.我们为什么要推行Code ...
- 如何优雅的使用RabbitMQ
RabbitMQ无疑是目前最流行的消息队列之一,对各种语言环境的支持也很丰富,作为一个.NET developer有必要学习和了解这一工具.消息队列的使用场景大概有3种: 1.系统集成,分布式系统的设 ...
- CorelDRAW X8 如何破解激活(附国际版安装包+激活工具) 2016-12-15
之前有位搞平面的好友“小瘦”说CDR X8无法破解,只能用X7.呃……呃……呃……好像是的 其实CDR8难激活主要在于一个点“没有离线激活了,只可以在线激活”,逆天不是专供逆向的,当然没能力去破解,这 ...
- bzoj3208--记忆化搜索
题目大意: 花花山峰峦起伏,峰顶常年被雪,Memphis打算帮花花山风景区的人员开发一个滑雪项目. 我们可以把风景区看作一个n*n的地图,每个点有它的初始高度,滑雪只能从高处往低处滑[严格大于] ...
- BPM嵌入式流程解决方案分享
一.需求分析由于企业业务的独特性或者企业高层独特的管理思想,很多客户选择了自行开发业务系统的方式来实现独有的竞争力. 这类信息系统通常经过了多年的开发,伴随着企业的发展一直在不断优化,与企业的业务非常 ...
- RunLoop 总结:RunLoop的应用场景(一)
参考资料 好的书籍都是值得反复看的,那好的文章,好的资料也值得我们反复看.我们在不同的阶段来相同的文章或资料或书籍都能有不同的收获,那它就是好文章,好书籍,好资料.关于iOS 中的RunLoop资料非 ...
- BZOJ 4196: [Noi2015]软件包管理器 [树链剖分 DFS序]
4196: [Noi2015]软件包管理器 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1352 Solved: 780[Submit][Stat ...