双重for循环优化

项目中有段代码逻辑是个双重for循环,发现数据量大的时候,直接导致数据接口响应超时,这里记录下不断优化的过程,算是抛砖引玉吧~

Talk is cheap,show me your code!

双重for循环优化

1、数据准备

Order

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Order {
    private Integer orderId;
    private String orderName;
}

OrderDetail

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class OrderDetail {

    private Integer orderDetailId;

    private Integer orderId;

    private String orderNums;

    private String orderAddress;
}

造测试数据

java 复制代码
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循环

java 复制代码
@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条件

java 复制代码
	@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、使用迭代器来删除内层循环中已使用或判断过的元素,减少循环次数

java 复制代码
	@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)来实现检索

java 复制代码
	@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集合特性均能够有效提升查询效率。

相关推荐
sg_knight3 小时前
Spring 框架中的 SseEmitter 使用详解
java·spring boot·后端·spring·spring cloud·sse·sseemitter
郑州光合科技余经理5 小时前
同城系统海外版:一站式多语种O2O系统源码
java·开发语言·git·mysql·uni-app·go·phpstorm
一只乔哇噻5 小时前
java后端工程师+AI大模型开发进修ing(研一版‖day60)
java·开发语言·人工智能·学习·语言模型
Dolphin_Home6 小时前
笔记:SpringBoot静态类调用Bean的2种方案(小白友好版)
java·spring boot·笔记
MetaverseMan6 小时前
Java虚拟线程实战
java
浪潮IT馆7 小时前
Tomcat运行war包的问题分析与解决步骤
java·tomcat
悟能不能悟7 小时前
Caused by: java.sql.SQLException: ORA-28000: the account is locked怎么处理
java·开发语言
_院长大人_7 小时前
MyBatis Plus 分批查询优化实战:优雅地解决 IN 参数过多问题(实操)
java·mybatis
C雨后彩虹8 小时前
机器人活动区域
java·数据结构·算法·华为·面试