Java 根据对象属性连接两个 List
概述
有时候我们需要对两个 List 或者 Collection 中的对象根据某个属性建立关联,关联的方式就像是数据库中的一对一或者一对多。使用 sql 语句可以直接完成关联,但是有时候我们希望查出两个 List,然后使用 Java 代码手动关联,这些代码往往是样板化的,能不能写一个相对通用的方法呢?这便是本文的目标。
场景描述
假设有两张表:user 表和 order 表,表中的数据如下:
Java 代码中可以分别定义 User 和 Order 作为表的实体类:
// 省略 getter and setter
// 省略 constructor
public class User {
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public class Order {
private int id;
private int userId;
private String product;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", userId=" + userId +
", product='" + product + '\'' +
'}';
}
}
Java 实现一对一 Join
首先我们假设 User 和 Order 对象是一对一的关系,为了找到 User 和 Order 的关系,通常的做法如下:
// 首先建立 userId 和 Order 对象的映射关系
Map<Integer, Order> userId2OrderMap = orders.stream().collect(Collectors.toMap(Order::getUserId, e -> e));
for (User user : users) {
int userId = user.getId();
// 根据 user 对象的 id 去 userId2OrderMap 中找到对应的 order 对象
Order order = userId2OrderMap.get(userId);
}
我们可以抽象一个 join 方法来简化这段样板化的代码
import java.util.Collection;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
class JoinUtil {
public static <T, E, K> void join(Collection<T> main,
Collection<E> sub,
Function<? super T, K> mainKeyMapper,
Function<? super E, K> subKeyMapper,
BiConsumer<? super T, ? super E> pairHandler) {
Map<K, E> map = sub.stream().collect(Collectors.toMap(subKeyMapper, e -> e, (a, b) -> a));
for (T t : main) {
K k = mainKeyMapper.apply(t);
E e = map.get(k);
pairHandler.accept(t, e);
}
}
}
完整代码,请移步 Github 查看。
mainKeyMapper 作用于 main collection 中的每个元素,目的是获取连接字段的值;subKeyMapper 作用于 sub collection,用于是用于建立一个 map,map 的 key 是通过 subKeyMapper 作用于 sub collection 中的元素获得的,value 则是被作用的元素本身。
pairHandler 是一个回调方法,可以传递一个 lambda 表达式来接收一对匹配的对象。下面我们来看看它的用法。
public static void main(String[] args) {
List<User> users = new ArrayList<>();
users.add(new User(1, "lucy"));
users.add(new User(2, "john"));
List<Order> orders = new ArrayList<>();
orders.add(new Order(1, 2, "laptop"));
orders.add(new Order(2, 1, "mouse"));
JoinUtil.join(users, orders, User::getId, Order::getUserId, (user, order) -> {
System.out.println(user + "matched order is: " + order);
});
}
// 输出如下
User{id=1, name='lucy'}matched order is: Order{id=2, userId=1, product='mouse'}
User{id=2, name='john'}matched order is: Order{id=1, userId=2, product='laptop'}
Java 实现一对多 Join
实现一对多也很简单,由于是一对多,因此 BiConsumer 的第二个泛型参数为 List。这里直接给出代码实现:
public static <T, E, K> void joinM(Collection<T> main,
Collection<E> sub,
Function<? super T, K> mainKeyMapper,
Function<? super E, K> subKeyMapper,
BiConsumer<? super T, List<? super E>> pairHandler) {
Map<K, List<E>> map = sub.stream().collect(Collectors.groupingBy(subKeyMapper));
for (T t : main) {
K k = mainKeyMapper.apply(t);
List<E> e = map.get(k);
pairHandler.accept(t, e);
}
}
下面是 joinM 的使用方法,users 和 orders 和 上面的数据相同,因此不再赘述。
JoinUtil.joinM(users, orders, User::getId, Order::getUserId, (user, matchOrders) -> {
System.out.println(user + "matched orders is: " + matchOrders);
});
// 输出如下
User{id=1, name='lucy'} matched orders is: [Order{id=2, userId=1, product='mouse'}]
User{id=2, name='john'} matched orders is: [Order{id=1, userId=2, product='laptop'}]
总结
本文介绍了如何使用 Java 代码实现类似数据中的一对一和一对多连接,并给出了两个 join util 方法的实现,利用这两个方法可以帮助我们消除样板化代码。
温馨提示:反馈需要登录