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

相关推荐
二两小咸鱼儿31 分钟前
Java Demo - JUnit :Unit Test(Assert Methods)
java·后端·junit
字节源流35 分钟前
【spring】配置类和整合Junit
java·后端·spring
跪在镜子前喊帅1 小时前
【面试】Java 多线程
java·面试
好看资源平台2 小时前
Java/Kotlin逆向基础与Smali语法精解
java·开发语言·kotlin
zimoyin2 小时前
解决 Java/Kotlin 资源加载问题
java·python·kotlin
阿木看源码3 小时前
bindingAdapter的异常错误
java·开发语言
跪在镜子前喊帅3 小时前
【面试】框架
java·面试
~Yogi3 小时前
每日学习Java之一万个为什么
java·开发语言·学习
Simon523144 小时前
数据结构---八大排序
java·数据结构·排序算法
WeiLai11124 小时前
面试基础--高并发高可用架构深度实践:降级熔断(Hystrix vs Sentinel)核心原理与源码解析
java·分布式·后端·hystrix·面试·架构·sentinel