背景是这样的:我们的项目中,定义了各种各样的和表对应的实体类。我们的逻辑中,经常会查出某个表的数据,然后按照这个表的某个字段进行分组。例如,A表,有属性ID和姓名name及其它属性,我们查出一批数据后,想按照name进行分组,生成Map
/**
* 按name分组方法
* @param list A表实体的列表
* @param map 分组后的存储map
*/
public static void groupA(List list, Map> map) {
if (null == list || null == map) {
return;
}
// 按name开始分组
String key;
List listTmp;
for (AEntity val : list) {
key = val.getName();
listTmp = map.get(key);
if (null == listTmp) {
listTmp = new ArrayList();
map.put(key, listTmp);
}
listTmp.add(val);
}
}
/**
* 按age分组方法
* @param list B表实体的列表
* @param map 分组后的存储map
*/
public static void groupB(List list, Map> map) {
if (null == list || null == map) {
return;
}
// 按age开始分组
String key;
List listTmp;
for (AEntity val : list) {
key = val.getAge();
listTmp = map.get(key);
if (null == listTmp) {
listTmp = new ArrayList();
map.put(key, listTmp);
}
listTmp.add(val);
}
}
于是就各种思考,总结出了几个特点:
1. 入参泛型不同
2. 分组的维度(属性方法)不同
如果能把这两个不同点统一起来,是不是就可以提取一个共同的工具类方法了?
思路也简单:入参泛型不同,那方法就使用泛型;分组使用的方法不同,就用反射机制,获取方法。于是有了初版的通用方法:
/**
* 将List按照V的某个方法返回值(返回值必须为K类型)分组,合入到Map>中
* 要保证入参的method必须为V的某一个有返回值的方法,并且该返回值必须为K类型
*
* @param list 待分组的列表
* @param map 存放分组后的map
* @param method 方法
*/
@SuppressWarnings("unchecked")
public static void listGroup2Map(List list, Map> map, Method method) {
// 入参非法行校验
if (null == list || null == map || null == method) {
LOGGER.error("CommonUtils.listGroup2Map 入参错误,list:" + list + " ;map:" + map
+ " ;method:" + method);
return;
}
try {
// 开始分组
Object key;
List listTmp;
for (V val : list) {
key = method.invoke(val);
listTmp = map.get(key);
if (null == listTmp) {
listTmp = new ArrayList();
map.put((K) key, listTmp);
}
listTmp.add(val);
}
} catch (Exception e) {
LOGGER.error("分组失败!", e);
}
}
/**
* 根据类和方法名,获取方法对象
*
* @param clazz
* @param methodName
* @return
*/
public static Method getMethodByName(Class> clazz, String methodName) {
Method method = null;
// 入参不能为空
if (null == clazz || StringUtils.isBlank(methodName)) {
LOGGER.error("CommonUtils.getMethodByName 入参错误,clazz:" + clazz + " ;methodName:"
+ methodName);
return method;
}
try {
method = clazz.getDeclaredMethod(methodName);
} catch (Exception e) {
LOGGER.error("类获取方法失败!", e);
}
return method;
}
到这里,我想分享的代码主体思路已经出来了。考虑到让调用者每次都调用两个方法,不太友好,就又改了一版,又补充增加了一个方法:
/**
* 将List按照V的methodName方法返回值(返回值必须为K类型)分组,合入到Map>中
* 要保证入参的method必须为V的某一个有返回值的方法,并且该返回值必须为K类型
*
* @param list 待分组的列表
* @param map 存放分组后的map
* @param clazz 泛型V的类型
* @param methodName 方法名
*/
public static void listGroup2Map(List list, Map> map, Class clazz, String methodName) {
// 入参非法行校验
if (null == list || null == map || null == clazz || StringUtils.isBlank(methodName)) {
LOGGER.error("CommonUtils.listGroup2Map 入参错误,list:" + list + " ;map:" + map
+ " ;clazz:" + clazz + " ;methodName:" + methodName);
return;
}
// 获取方法
Method method = getMethodByName(clazz, methodName);
// 非空判断
if (null == method) {
return;
}
// 正式分组
listGroup2Map(list, map, method);
}
测试方法如下:
@Test
public void testGroup() {
AEntity a1 = new AEntity();
a1.setId("111");
a1.setName("name1");
AEntity a2 = new AEntity();
a2.setId("222");
a2.setName("name");
AEntity a3 = new AEntity();
a3.setId("111");
a3.setName("name3");
AEntity a4 = new AEntity();
a4.setId("222");
a4.setName("name");
List list = new ArrayList();
list.add(a1);
list.add(a2);
list.add(a3);
list.add(a4);
list.add(a5);
System.out.println("list分组前为:" + list);
Map> map = new HashMap>();
CommonUtils.listGroup2Map(list, map, AEntity.class, "getName");// 输入方法名
System.out.println("分组完成,分组后的map为:" + map);
}
也许某些开源的工具类中已经有过这样的方法了,不过我没看到,就自己总结了一把,希望对大家有所帮助。
最后再碎碎念一把:泛型不支持类似V.class这样的调用,不然还能省掉Class