JAVA设计模式-大集合数据拆分

背景

我们在做软件开发时,经常会遇到把大集合的数据,拆分成子集合处理。例如批量数据插入数据库时,一次大约插入5000条数据比较合理,但是有时候待插入的数据远远大于5000条。这时候就需要进行数据拆分。数据拆分基本逻辑并不复杂,下面尝试把数据拆分逻辑封装一下。

拆分逻辑

拆分过程唯一要求就是数据不能遗漏,也不能重复处理。

  • 定义子集合大小
  • 遍历源数据集合,达到一个子集合大小,
  • 根据业务需要开始处理子集合数据
  • 直到处理完所有数据

代码

先实现基本功能代码

java 复制代码
	/**
     * @param dataList 原数据集合
     * @param subSize  子集合size
     * @throws Exception
     */
	public static <T> void processdSubData(List<T> dataList, int subSize) throws Exception {
      
      	//子集合对象
        List<T> subDataList = new ArrayList<>();
		//计数变量
        int count = 0;
        
        for (T t : dataList) {
			
            subDataList.add(t);
            count++;//累计子集合数据数量

            if (count >= subSize) {//这里可以使用等号==,个人习惯使用大于等于>=

                try {
                	//处理子集合数据
                    //doSomeThing(subDataList);
                } catch (Exception e) {
                    throw e;
                } finally {
                	//清空计数变量和子集合
                    count = 0;
                    subDataList.clear();
                }

            }
        }

		//这里的剩余数据处理,非常容易遗漏,这也是为什么要封装公共代码的一个原因
		//封装成公共代码后,就不用担心遗漏这一部分数据
        if (subDataList.size() > 0) {
            //最后一次剩余数据量小于subSize,这里再处理一次
            try {
                //处理子集合数据
                //doSomeThing(subDataList);
            } catch (Exception e) {
                throw e;
            }
        }
    }

以上的代码,逻辑清晰且没有复杂的索引计算,是个比较好的实现。但是代码没有通用性,每次遇到数据拆分,都要写一遍拆分呢逻辑,写的多了难免出问题。仔细看下代码,除了处理子集合数据的业务代码方法,其他代码都是一样的。下面改造一下,子集合数据的业务方法由外部传入。那么拆分逻辑部分就可以通用,不用担心出问题了。

新实现

  • 业务处理接口
java 复制代码
package cn.com.soulfox.common.functions.splitdata;

import java.util.List;

/**
 *
 * 子数据集合业务数据处理接口
 * @create 2024/6/24 10:21
 */
@FunctionalInterface//函数式接口,只有一个抽象方法
public interface SplitDataCallback<T> {

    void splitDataProcess(List<T> subDataList);
}
  • 拆分工具类
java 复制代码
package cn.com.soulfox.common.functions.splitdata;


import java.util.List;

/**
 * 大集合拆分处理
 *
 * 
 * @create 2024/6/24 10:35
 */
public class SplitDataListUtil {

    /**
     * @param dataList 待拆分数据集合
     * @param subSize  子集合的size
     * @param callback 子集合数据处理类
     * @throws Exception
     */
    public static <T> void processData(List<T> dataList, int subSize, SplitDataCallback<T> callback) throws Exception {
    	//如果不做成公共代码,下面的判空的代码,忙的时候就不会写了吧 -:)
        if (callback == null) {
            //处理类为空
            return;
        }
        if (dataList == null || dataList.isEmpty()) {
            //数据集合为空
            return;
        }
        if (subSize <= 0) {
            //子集长度小于等于 0
            return;
        }
		
        if (subSize >= dataList.size()) {
            //子集长度大于等于原集合,不需要拆分,直接处理
            try {
                callback.splitDataProcess(dataList);
            } catch (Exception e) {
                System.out.println("处理子数据集失败:"+e.getMessage());
                throw e;
            }
            return;
        }

        processdSubData(dataList, subSize, (SplitDataCallback<T>) callback);
    }

    /**
     * @param dataList 原数据集合
     * @param subSize  子集合size
     * @param callback 子集合数据处理类
     * @throws Exception
     */
    private static <T> void processdSubData(List<T> dataList, int subSize, SplitDataCallback<T> callback) throws Exception {
       //子集合对象
        List<T> subDataList = new ArrayList<>();
        int count = 0;
        for (T t : dataList) {

            subDataList.add(t);
            //计数
            count++;

            if (count >= subSize) {//这里可以使用等号==,个人习惯使用大于等于>=
				//数量达到subSize,做一次处理
                try {
                    callback.splitDataProcess(subDataList);
                } catch (Exception e) {
                    System.out.println("处理子数据集失败:"+e.getMessage());
                    throw e;
                } finally {
                	//清空计数变量和子集合
                    count = 0;
                    subDataList.clear();
                }

            }
        }

		//这里的剩余数据处理,非常容易遗漏,这也是为什么要封装公共代码的一个原因
		//封装成公共代码后,就不用担心遗漏这一部分数据
        if (subDataList.size() > 0) {
            //最后一次剩余数据量小于subSize,这里再处理一次
            try {
                callback.splitDataProcess(subDataList);
            } catch (Exception e) {
                System.out.println("处理子数据集失败:"+e.getMessage());
                throw e;
            }
        }
    }
}
  • 单元测试
java 复制代码
package cn.com.soulfox.common.functions.splitdata;

import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

/**
 *
 * @create 2024/6/24 15:50
 */
public class SplitDataListUtilTest {

    private List<String> dataList;

    @Before
    public void setup(){
        //准备数据
        dataList = Arrays.asList("a","b","c","1","2");
    }

    @Test
    public void test(){
        //定义子集合size
        int subSize = 2;
        //业务逻辑比较简单, 可直接写业务代码
        try {
            SplitDataListUtil.processData(this.dataList, subSize,
                    (subDataList -> {
                        System.out.println("简单业务代码++++");
                        subDataList.forEach(data ->{
                            System.out.println("简单业务代码: "+data);
                        });
                    }));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 业务处理逻辑复杂
    实现类
java 复制代码
package cn.com.soulfox.common.functions.splitdata;

import java.util.List;

/**
 * 业务逻辑复杂
 * @create 2024/6/24 16:05
 */
public class ComplexBusinessImpl implements SplitDataCallback<String>{

    @Override
    public void splitDataProcess(List<String> subDataList) {
        System.out.println("复杂业务代码++++");
        subDataList.forEach(data ->{
            System.out.println("复杂业务代码: "+data);
        });
    }

}

加一个测试方法

java 复制代码
	@Test
    public void testComplexBusiness(){
        //定义子集合size
        int subSize = 2;
        //业务逻辑比较复杂, 创建接口实现类ComplexBusinessImpl 传入方法中
        ComplexBusinessImpl complexBusiness = new ComplexBusinessImpl();
        try {
            SplitDataListUtil.processData(this.dataList, subSize, complexBusiness);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

测试结果

总结一下。。。

拆分数据功能并不复杂,封装公共代码,也看不什么好处,实际开发的时候直接复制拆分代码即可。

这里主要是为了提出一种,设计通用功能的思路。任何功能,总有一部分结构性代码是不变的,变化的是业务处理代码。例如,上面的例子中,把大集合拆分成小集合的逻辑是不变的,变化的是数据处理逻辑。把不变的部分抽象出来封装成公共代码,同时把一些判空,边界数据做一下统一处理,这样就会在提高代码复用率的同时,减少出错几率。

相关推荐
咖啡の猫3 分钟前
设计模式概述
设计模式
庞传奇5 分钟前
【LC】191. 位1的个数
java·数据结构·算法·leetcode
玉带湖水位记录员7 分钟前
命令模式——C++实现
c++·设计模式·命令模式
禁默1 小时前
深入浅出:Java 抽象类与接口
java·开发语言
小万编程2 小时前
【2025最新计算机毕业设计】基于SSM的医院挂号住院系统(高质量源码,提供文档,免费部署到本地)【提供源码+答辩PPT+文档+项目部署】
java·spring boot·毕业设计·计算机毕业设计·项目源码·毕设源码·java毕业设计
白宇横流学长2 小时前
基于Java的银行排号系统的设计与实现【源码+文档+部署讲解】
java·开发语言·数据库
123yhy传奇2 小时前
【学习总结|DAY027】JAVA操作数据库
java·数据库·spring boot·学习·mybatis
想要打 Acm 的小周同学呀2 小时前
亚信科技Java后端外包一面
java·求职·java后端
lishiming03086 小时前
TestEngine with ID ‘junit-jupiter‘ failed to discover tests 解决方法
java·junit·intellij-idea
HEU_firejef6 小时前
设计模式——工厂模式
java·开发语言·设计模式