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

相关推荐
独立开阀者_FwtCoder1 分钟前
"页面白屏了?别慌!前端工程师必备的排查技巧和面试攻略"
java·前端·javascript
Touper.6 分钟前
JavaSE -- 泛型详细介绍
java·开发语言·算法
静若繁花_jingjing23 分钟前
Redis线程模型
java·数据库·redis
hello早上好44 分钟前
CGLIB代理核心原理
java·spring
魔镜魔镜_谁是世界上最漂亮的小仙女1 小时前
java-web开发
java·后端·架构
爱吃小土豆豆豆豆1 小时前
定时器和守护线程
java
Seven971 小时前
了解GC吗?什么是GC?
java
Edingbrugh.南空1 小时前
Flink ClickHouse 连接器维表源码深度解析
java·clickhouse·flink
掘金-我是哪吒2 小时前
分布式微服务系统架构第157集:JavaPlus技术文档平台日更-Java多线程编程技巧
java·分布式·微服务·云原生·架构
飞翔的佩奇2 小时前
Java项目:基于SSM框架实现的忘忧小区物业管理系统【ssm+B/S架构+源码+数据库+毕业论文+开题报告】
java·数据库·mysql·vue·毕业设计·ssm框架·小区物业管理系统