背景
在工作的过程中经常遇到一种场景:某个查询要求效率比较高,而且需要的数据分布在不同的表之中,往往我们需要查询一个主表拿到数据之后,再拿主表的id或者其它识别符号去查询从表的数据。
为了查询效率比较高,那么我们查从表的时候如果使用for循环去查找效率会比较慢,这个可以验证。这个时候推荐使用in语句查询一次性把所有的结果找出来再在内存中做匹配。
这里我需要做一个主副表的匹配,把副表的值映射到主表之中。
模拟主副表
主表Parent
import java.util.List;
public class Parent {
private Long id;
private String code;
private String name;
List<Child> children;
Child oneChild;
public Parent(Long id, String code, String name) {
this.id = id;
this.code = code;
this.name = name;
}
public Child getOneChild() {
return oneChild;
}
public void setOneChild(Child oneChild) {
this.oneChild = oneChild;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
副表Child
public class Child {
public Child(Long id, Long parentId, String name, String code) {
this.id = id;
this.parentId = parentId;
this.name = name;
this.code = code;
}
private Long id;
private Long parentId;
private String name;
private String code;
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
对应的映射代码
public final class ListBeanMap {
/**
* 一对多映射
* @param parents 父节点
* @param children 子节点
* @param parentId 父节点对应子节点的字段
* @param childId 子节点对应父节点的字段
* @param action 父节点的set方法,将子节点对应的数据set到父节点
* @param <T> 父节点的类型
* @param <K> 子节点的类型
* @param <U> 父子节点使用何种类型来做对应
* @return 返回值只是为了在return的时候比较方便,其实在做映射的时候,对应的值已经set到父节点之中。
*/
public static <T,K,U> List<T> one2Many(List<T> parents, List<K> children,
Function<? super T, ? extends U> parentId,
Function<? super K, ? extends U> childId,
BiConsumer<? super T,? super List<K>> action) {
if (CollectionUtils.isEmpty(parents) || CollectionUtils.isEmpty(children)) {
return new ArrayList<>();
}
Map<U, T> pMap = parents.stream().collect(Collectors.toMap(parentId, p -> p));
Map<U, List<K>> cMap = children.stream().collect(Collectors.toMap(childId,
Lists::newArrayList,
(List<K> oldList, List<K> newList) -> {
oldList.addAll(newList);
return oldList;
}));
pMap.forEach((k,v) -> action.accept(v, cMap.get(k)));
return parents;
}
/**
* 一对一映射
* @param parents 父节点
* @param children 子节点
* @param parentId 父节点对应子节点的字段
* @param childId 子节点对应父节点的字段
* @param action 父节点的set方法,将子节点对应的数据set到父节点
* @param <T> 父节点的类型
* @param <K> 子节点的类型
* @param <U> 父子节点使用何种类型来做对应
* @return 返回值只是为了在return的时候比较方便,其实在做映射的时候,对应的值已经set到父节点之中。
*/
public static <T,K,U> List<T> one2One(List<T> parents, List<K> children,
Function<? super T, ? extends U> parentId,
Function<? super K, ? extends U> childId,
BiConsumer<? super T,? super K> action) {
if (CollectionUtils.isEmpty(parents) || CollectionUtils.isEmpty(children)) {
return new ArrayList<>();
}
Map<U, T> pMap = parents.stream().collect(Collectors.toMap(parentId, p -> p));
Map<U, K> cMap = children.stream().collect(Collectors.toMap(childId, c -> c));
pMap.forEach((k,v) -> action.accept(v, cMap.get(k)));
return parents;
}
}
测试类
public static void main(String[] args) {
List<Parent> parents = new ArrayList<>(1000);
List<Child> children = new ArrayList<>(1000);
for (long i = 0; i < 1000; i++) {
parents.add(new Parent(i,String.format("%010d", (long)(100000000 * Math.random())), "test"+i));
children.add(new Child(1000-i, i,"test"+(1000-i), String.format("%010d", (long)(100000000 * Math.random()))));
}
ListBeanMap.one2Many(parents, children, Parent::getId, Child::getParentId, Parent::setChildren);
ListBeanMap.one2One(parents, children, Parent::getId, Child::getParentId, Parent::setOneChild);
System.out.println(JSON.toJSONString(parents));
}
测试结果(另外几种测试场景因为时间关系就不做了,如果发现问题,欢迎留言):