List分段浅谈

217 阅读2分钟

「这是我参与2022首次更文挑战的第32天,活动详情查看:2022首次更文挑战」。

一、相关概念

不知道各位大佬在开发中有没有遇到过下面所说的这种情况,查询出一堆的数据后,这堆数据是一个List对象,对象中有一个属性id,我们需要通过id去查询另一个表的数据(为什么不关联查询,部分情况下不能关联查询,或者通过这个id需要执行一个多表关联的查询,关联在原来的表上导致查询速度太慢,需要优化)。此时如何处理呢?循环去查询,但如果List很长呢,就会导致查询数据超级慢。此时想到通过List获得一个id的list,在sql中通过in进行查询。但如果id的list数量很多,可能会导致sql长度超出。如何解决这个问题呢?这时候就可以对这个list进行分段处理,一段一段的去查询。

二、List分段的实现方式

/**
 * 模拟通过id获取的数据
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class IdData {

    private Integer id;

    private Integer price;
}

模拟我们通过id去查询数据后的返回对象。

(一)、使用List的subList方法

public class ListSectionTest {
    private final static List<Integer> idList = new ArrayList<>();

    static {
        idList.add(1);
        idList.add(2);
        idList.add(3);
        idList.add(4);
        idList.add(5);
        idList.add(6);
        idList.add(7);
        idList.add(8);
        idList.add(9);
        idList.add(10);
    }

    public static void main(String[] args) {
        List<List<Integer>> idPartition = subSection(idList, 2);
        List<IdData> dataList = new ArrayList<>();
        idPartition.forEach(partition -> dataList.addAll(imitateQueryById(partition)));
        System.out.println(dataList);
    }

    public static List<IdData> imitateQueryById(List<Integer> list) {
        return list.stream().map(it -> IdData.builder().id(it).price((int) (Math.random() * 10)).build()).collect(Collectors.toList());
    }


    public static <T> List<List<T>> subSection(List<T> list, Integer partSize) {
        int length = list.size();
        //加partSize-1,相当于加不满一组。
        int groupNum = (length + partSize - 1) / partSize;
        List<List<T>> result = new ArrayList<>(groupNum);
        for (int i = 0; i < groupNum; i++) {
            int fromIndex = i * partSize;
            int endIndex = Math.min((i + 1) * partSize, length);
            result.add(list.subList(fromIndex, endIndex));
        }
        return result;
    }
}

可以看到,我们通过subSection把List分成了5个片段,或者说5组。然后每组再去获取数据。我们是通过List的subList方法进行实现的,通过计算idList可以被分成几个组,算出每个组的开始位置和结束位置,最后获得分组结果。imitateQueryById方法是模拟我们通过id查询数据库的数据。 image.png

(二)、引入第三方工具类

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>31.0.1-jre</version>
</dependency>

引入jar包

public static void main(String[] args) {
//        List<List<Integer>> idPartition = subSection(idList, 2);
        List<List<Integer>> idPartition = Lists.partition(idList, 2);
        List<IdData> dataList = new ArrayList<>();
        idPartition.forEach(partition -> {
            System.out.println(partition);
            dataList.addAll(imitateQueryById(partition));
        });
        System.out.println(dataList);
    }

使用第三方工具的Lists.partition方法进行分组,省去了我们自定义分组方法的过程。

三、总结

List<List<Integer>>有点像二维数组,但是比二维数组更加的灵活,因为嵌套的第二个List的长度是可以变化的。