双重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集合特性均能够有效提升查询效率。

相关推荐
geats人山人海8 分钟前
java 4继承
java·开发语言
众链网络31 分钟前
Agent 的成本大头不在“写“,在“判断“:拆解专用决策模型的三层架构
java·开发语言·人工智能·微服务
周小董34 分钟前
[1371]jdk管理工具
java·开发语言
青春逝如流水1 小时前
Java 中线程的创建方式:从基础到现代
java·开发语言·多线程·ai文章
Peregrine91 小时前
C++ - > 类和对象
java·c++·算法
2501_933923251 小时前
SpringCloud组件:Eureka服务注册与发现
java·开发语言·后端·spring cloud·eureka
Wang's Blog2 小时前
Java 中间件之 RabbitMQ 快速入门: SpringAMQP 消息转换器
java·中间件·java-rabbitmq
Re_zero2 小时前
一次 SSE 断流排查:PrintWriter 吞掉了我的 IOException
java·后端
我是人✓2 小时前
Java核心语法:作用域、循环与大数值处理
java·开发语言·后端
夜之眷属3 小时前
PriorityQueue:for-each 遍历和 poll 取出,顺序一样吗?
java·服务器·数据结构·后端·算法