关于Java中大数据集使用List接口的removeAll效率提升

因实际业务需求,在一个项目中需要对两个大数据集需要对两个List集合中的交集进行遍历并移除,保留差异;
可能出现的情况如下:

  1. ListA中包含一部分ListB的集合的数据
  2. ListB中包含一部分ListA的集合的数据
  3. ListA中完全不包含ListB的集合的数据
  4. ListB中完全不包含ListA的集合的数据

以下是未优化之前的代码(性能极差):

long startTime, endTime;
@Test
public void test01() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    SaleCwjkMapper mapper = sqlSession.getMapper(SaleCwjkMapper.class);
    startTime = System.currentTimeMillis();
    List<SaleCwjk> list01 = mapper.queryTest01(); // 140702
    List<SaleCwjk> list02 = mapper.queryTest02(); // 220111
    endTime = System.currentTimeMillis();
    System.out.println("查询耗时:" + (endTime - startTime)/1000 + " S");

    startTime = System.currentTimeMillis();
    Collection collectionA = new ArrayList<SaleCwjk>(list01);
    Collection collectionB = new ArrayList<SaleCwjk>(list02);
    Collection collectionC = new ArrayList<SaleCwjk>(list01);
    Collection collectionD = new ArrayList<SaleCwjk>(list02);
    Collection collectionE = new ArrayList<SaleCwjk>();
    endTime = System.currentTimeMillis();
    System.out.println("生成集合耗时:" + (endTime - startTime)/1000 + " S");

    startTime = System.currentTimeMillis();
    collectionA.removeAll(collectionB);
    collectionD.removeAll(collectionC);
    endTime = System.currentTimeMillis();
    System.out.println("移除集合耗时:" + (endTime - startTime)/1000 + " S");

    startTime = System.currentTimeMillis();
    collectionE.addAll(collectionA);
    collectionE.addAll(collectionD);
    endTime = System.currentTimeMillis();
    System.out.println("添加新集合耗时:" + (endTime - startTime)/1000 + " S");
    System.out.println(collectionE.size());

    sqlSession.close();
}

上面代码中结合 MyBatis 查询了并返回一个List数据集,其中list01的数据集的大小为:140702;list02的数据集大小为:220111;查询结果如下:
未优化之前的代码运行结果
运行结果就很离谱了,两个14W+和22W+的数据集需要587S才能移除完毕;

以下是优化之后的代码:
自定义一个removeAll方法

/**
 * @Description: list 剔除 指定值
 * @param data 数据源
 * @param beDelete 被剔除的数据
 * @return
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<SaleCwjk> removeAll(List<SaleCwjk> data, List<SaleCwjk> beDelete) {
    LinkedList<SaleCwjk> linkedList = new LinkedList<SaleCwjk>(data);
    HashSet<SaleCwjk> hashSet = new HashSet<SaleCwjk>(beDelete);
    Iterator it = linkedList.iterator();
    while(it.hasNext()){
        if(hashSet.contains(it.next())){
            it.remove();
        }
    }
    return linkedList;
}

然后对test01代码进行优化:

long startTime, endTime;
@Test
public void test02() {
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    SaleCwjkMapper mapper = sqlSession.getMapper(SaleCwjkMapper.class);
    startTime = System.currentTimeMillis();
    List<SaleCwjk> list01 = mapper.queryTest01();//140702
    List<SaleCwjk> list02 = mapper.queryTest02();//220111
    endTime = System.currentTimeMillis();
    System.out.println("查询耗时:" + (endTime - startTime)/1000 + " S");

    startTime = System.currentTimeMillis();
    List<SaleCwjk> list03 = new ArrayList<SaleCwjk>(list01);
    List<SaleCwjk> list04 = new ArrayList<SaleCwjk>(list02);
    List<SaleCwjk> listResult = new ArrayList<SaleCwjk>();
    endTime = System.currentTimeMillis();
    System.out.println("生成集合耗时:" + (endTime - startTime)/1000 + " S");

    startTime = System.currentTimeMillis();
    List<SaleCwjk> result01 = this.removeAll(list01, list02);
    List<SaleCwjk> result02 = this.removeAll(list04, list03);
    endTime = System.currentTimeMillis();
    System.out.println("移除集合耗时:" + (endTime - startTime)/1000 + " S");

    startTime = System.currentTimeMillis();
    listResult.addAll(result01);
    listResult.addAll(result02);
    endTime = System.currentTimeMillis();
    System.out.println("添加新集合耗时:" + (endTime - startTime)/1000 + " S");
    System.out.println(list01.size());
    System.out.println(list02.size());
    System.out.println(listResult.size());

    sqlSession.close();
}

代码优化后的运行结果如下:
代码优化后的运行结果
我不禁很诧异,由原先的587秒到优化后的0秒,这个操作时间完全不是一个概念;网上搜索了一些资料才稍微明白了:

  • ArrayList 是基于数组实现的,循环遍历、读取效率高,插入和删除效率低,因为每次插入和删除都需要移动数组下标
  • LinkedList 是由双链,删除不需要移动数组元素,只需要修改链表节点指针
  • 使用Iterator 迭代器,只用对 iterator 进行循环,然后删除的元素,无需关注下标的问题,遍历效率高

优化的removeAll方法来自于:https://blog.csdn.net/zhaoqi_i/article/details/103719194

posted @   爱Ni说不出口  阅读(590)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示