项目中有段代码逻辑是个双重for循环,发现数据量大的时候,直接导致数据接口响应超时,这里记录下不断优化的过程,算是抛砖引玉吧~
Talk is cheap,show me your code!
双重for循环优化
- 1、数据准备
- 2、原始双重for循环
- 3、直接使用双重for循环查询条件,增加break条件
- 4、使用迭代器来删除内层循环中已使用或判断过的元素,减少循环次数
- 5、把要筛选的信息写成map集合,遍历List时用map.get(key)来实现检索
- 6、总结
1、数据准备
Order
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order {
private Integer orderId;
private String orderName;
}
OrderDetail
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class OrderDetail {
private Integer orderDetailId;
private Integer orderId;
private String orderNums;
private String orderAddress;
}
造测试数据
public static List<Order> getOrderTestList() {
List<Order> orders = new ArrayList<>();
for (int i = 1; i <= 50000; i++) {
Order order = new Order();
order.setOrderName(UUID.randomUUID().toString());
order.setOrderId(i);
orders.add(order);
}
return orders;
}
public static List<OrderDetail> getOrderDetailTestList() {
List<OrderDetail> orderDetails = new ArrayList<>();
for (int i = 30000; i >= 1; i--) {
OrderDetail orderDetail = new OrderDetail();
orderDetail.setOrderAddress(UUID.randomUUID().toString());
orderDetail.setOrderId(i);
orderDetail.setOrderDetailId(i);
orderDetails.add(orderDetail);
}
return orderDetails;
}
2、原始双重for循环
@Test
void test3() {
List<Order> orderTestList = getOrderTestList();
List<OrderDetail> orderDetailTestList = getOrderDetailTestList();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 直接使用双重for循环查询条件
for (Order order : orderTestList) {
int orderId = order.getOrderId();
for (OrderDetail orderDetail : orderDetailTestList) {
if(orderId == orderDetail.getOrderId() ){
System.out.println("模拟数据orderAddress 业务处理......" + orderDetail.getOrderAddress());
}
}
}
stopWatch.stop();
System.out.println("最终耗时" + stopWatch.getTotalTimeMillis());
}
执行结果
3、直接使用双重for循环查询条件,增加break条件
@Test
void test3() {
List<Order> orderTestList = getOrderTestList();
List<OrderDetail> orderDetailTestList = getOrderDetailTestList();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 直接使用双重for循环查询条件,增加break条件
for (Order order : orderTestList) {
int orderId = order.getOrderId();
for (OrderDetail orderDetail : orderDetailTestList) {
if(orderId == orderDetail.getOrderId() ){
System.out.println("模拟数据orderAddress 业务处理......" + orderDetail.getOrderAddress());
break;
}
}
}
stopWatch.stop();
System.out.println("最终耗时" + stopWatch.getTotalTimeMillis());
}
执行结果
4、使用迭代器来删除内层循环中已使用或判断过的元素,减少循环次数
@Test
void test3() {
List<Order> orderTestList = getOrderTestList();
List<OrderDetail> orderDetailTestList = getOrderDetailTestList();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 使用迭代器来删除内层循环中已使用或判断过的元素,减少循环次数
for (Order order : orderTestList) {
ListIterator<OrderDetail> orderDetailListIterator = orderDetailTestList.listIterator();
int orderId = order.getOrderId();
while (orderDetailListIterator.hasNext()) {
OrderDetail nextOrderDetail = orderDetailListIterator.next();
if(orderId == nextOrderDetail.getOrderId() ){
System.out.println("模拟数据orderAddress 业务处理......" + nextOrderDetail.getOrderAddress());
orderDetailListIterator.remove();
}
}
}
stopWatch.stop();
System.out.println("最终耗时" + stopWatch.getTotalTimeMillis());
}
执行结果
5、把要筛选的信息写成map集合,遍历List时用map.get(key)来实现检索
@Test
void test3() {
List<Order> orderTestList = getOrderTestList();
List<OrderDetail> orderDetailTestList = getOrderDetailTestList();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//使用stream() 记得一定要判空
Map<Integer, String> orderAddressMap =
orderDetailTestList.stream().collect(Collectors.toMap(OrderDetail::getOrderId, OrderDetail::getOrderAddress));
for (Order order : orderTestList) {
int orderId = order.getOrderId();
String orderAddress = orderAddressMap.get(orderId);
if (StringUtils.hasLength(orderAddress)) {
System.out.println("模拟数据orderAddress 业务处理......" + orderAddress);
}
}
if (StringUtils.hasLength(orderAddress)) {
System.out.println("模拟数据orderAddress 业务处理......" + orderAddress);
}
stopWatch.stop();
System.out.println("最终耗时" + stopWatch.getTotalTimeMillis());
}
测试结果
6、总结
可以看出,通过迭代删除或者利用map集合特性均能够有效提升查询效率。