优化一:尽量减少代码中的时间复杂度和数据库连接

67 阅读3分钟

前言

大家好,我是刺客阿七,该账号主要记录我工作以来的一些经验,毕业工作的第一篇文章,写的可能不太好,希望大家指正,一起进步学习。

正文

需求举例

​ 举一个比较简单的需求,众所周知,在电商项目管理平台肯定会有一个商品列表,商品列表会查询出商品数据和一些和商品相关的数据,这里以商品的供应商名称为例,但是商品表一般只会存供应商的id,这个时候我们还需要查询供应商的数据,看到这大家肯定会想,这还不简单,直接关联查询一下不就可以了吗,但是没这么简单,因为我们这个系统是一个微服务项目,商品模块和用户模块是不同的服务,对应的数据库也是不同的,所以得通过Feign来进行数据传递,但是这些对于本文来说都不重要,我们主要学习这个思想,以下是三种实现方式。

实体

1 商品实体部分属性和供应商实体部分属性 `

package com.decent.manager.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 商品测试属性
 *
 * @author lhh
 * @date 2024/4/1 14:39
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
    /**
     * 商品id
     */
    private Long id;
    /**
     * 商品名称
     */
    private String productName;
    /**
     * 供应商id
     */
    private Long supplierId;
}
package com.decent.manager.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 供应商测试属性
 *
 * @author lhh
 * @date 2024/4/1 14:39
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Supplier {
    /**
     * 供应商id
     */
    private Long id;
    /**
     * 供应商名称
     */
    private String supplierName;
}

2 返回给前端的vo

package com.decent.manager.vo;

import com.decent.manager.entity.Product;
import lombok.*;

/**
 * 商品VO
 *
 * @author lhh
 * @date 2024/4/1 15:42
 */
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductVO extends Product {

    /**
     * 供应商名称
     */
    private String supplierName;

    public ProductVO(Long id, String productName, Long supplierId) {
        super(id, productName, supplierId);
    }
}

实现一

遍历整个商品集合,通过循环调用fegin依次给每个商品的供应商赋值。

 // 模拟商品数据
        List<ProductVO> productList = Arrays.asList(
                new ProductVO(1L, "商品1", 1L),
                new ProductVO(2L, "商品2", 2L),
                new ProductVO(3L, "商品3", 3L));
        // 遍历商品,调用Feign查询,获取供应商数据然后通过id赋值
        productList.forEach(product -> {
            // 1 通过商品的供应商id,调用fegin查询该供应商的数据,因为没有fegin,new个对象假装查到了
            Supplier supplier = new Supplier();
            product.setSupplierName(supplier.getSupplierName());
        });

这样一看,功能确实没问题,但是大家都知道,调用fegin(或者是执行sql语句,因为要建立数据库连接)是比较消耗性能的,然后我还在循环里面调用,这显然是及其消耗性能的,所以,我们想想能不能只调用一次fegin或者数据库呢?

实现二

遍历整个商品集合,把所有供应商数据一次性查出来,然后通过循环供应商遍历赋值。

// 模拟商品数据
        List<ProductVO> productList = Arrays.asList(
                new ProductVO(1L, "商品1", 1L),
                new ProductVO(2L, "商品2", 2L),
                new ProductVO(3L, "商品3", 3L));
        // 通过fegin一次性把所有供应商数据查出来
        List<Supplier> supplierList = Arrays.asList(
                new Supplier(1L, "供应商1"),
                new Supplier(2L, "供应商2"),
                new Supplier(3L, "供应商3"));
        // 遍历商品,调用Feign查询,获取供应商数据然后通过id赋值
        productList.forEach(product -> {
            supplierList.forEach(supplier -> {
                product.setSupplierName(supplier.getSupplierName());
            });
        });

这样一看,确实减少了fegin的调用或者sql的执行次数,但是仔细一看,双层for循环,时间复杂度n*n?这样显然还不够好。

实现三

沿用实现二的代码,然后将供应商数据转为map,再通过map的get方法获取供应商数据,这样时间复杂度就是n了。

  // 模拟商品数据
        List<ProductVO> productList = Arrays.asList(
                new ProductVO(1L, "商品1", 1L),
                new ProductVO(2L, "商品2", 2L),
                new ProductVO(3L, "商品3", 3L));
        // 通过fegin一次性把所有供应商数据查出来
        List<Supplier> supplierList = Arrays.asList(
                new Supplier(1L, "供应商1"),
                new Supplier(2L, "供应商2"),
                new Supplier(3L, "供应商3"));
        Map<Long, String> supplierMap = supplierList.stream().collect(Collectors.toMap(Supplier::getId,
                Supplier::getSupplierName));
        // 遍历商品,调用Feign查询,获取供应商数据然后通过id赋值
        productList.forEach(product -> {
            product.setSupplierName(supplierMap.get(product.getSupplierId()));
        });
    }