• List获取差集产生的问题


    背景

    在批量保存数据的时候,需要和数据库进行比对。如果当前数据存在则从List集合中删除。

    List批量删除的话,一般都会考虑使用removeAll。

    操作流程如下:

    1、将现有批量数据的外部唯一字段和数据库中的数据进行匹配,返回现有存在的List对象

    2、使用removeAll功能,批量删除,取得当前差集。

    使用removeAll功能后,在批量保存的时候,还是直接导致了数据重复。

    Api 说明

    操作类型                  

    方法说明
    交集listA.retainAll(listB) 调用方法后ListA变为两个集合的交集,ListB不变
    差集listA.removeAll(listB) 调用方法后ListA变为两个集合的差集,ListB不变
    并集1.listA.removeAll(listB) 2.listA.addAll(listB)  去重,先取差集再并集。ListA变为两个集合的并集,ListB不变

    removeAll源码

     说啥都是虚的,翻removeAll源码牌子。

    1. public boolean removeAll(Collection c) {
    2. return batchRemove(c, false, 0, size);
    3. }
    4. ...
    5. boolean batchRemove(Collection c, boolean complement,
    6. final int from, final int end) {
    7. Objects.requireNonNull(c);
    8. final Object[] es = elementData;
    9. int r;
    10. // Optimize for initial run of survivors
    11. for (r = from;; r++) {
    12. if (r == end)
    13. return false;
    14. if (c.contains(es[r]) != complement)
    15. break;
    16. }
    17. int w = r++;
    18. try {
    19. for (Object e; r < end; r++)
    20. if (c.contains(e = es[r]) == complement)
    21. es[w++] = e;
    22. } catch (Throwable ex) {
    23. // Preserve behavioral compatibility with AbstractCollection,
    24. // even if c.contains() throws.
    25. System.arraycopy(es, r, es, w, end - r);
    26. w += end - r;
    27. throw ex;
    28. } finally {
    29. modCount += end - w;
    30. shiftTailOverGap(es, w, end);
    31. }
    32. return true;
    33. }

    我们可以看到,需要循环比较每个对象。

    1. for (r = from;; r++) {
    2. if (r == end)
    3. return false;
    4. if (c.contains(es[r]) != complement)
    5. break;
    6. }

    从数据库查出来的数据,包含了ID等其它字段。这样两个对象的属性就不一样了。所以会返回false。这样就达不到去重的目的了。

    自定义对象就可以使用下面JDK8+的Stream方式去去重了。

    将已经在库的数据(exitList)和需要保存的数据(entityList)匹配,将不存在库里的挑出来放到新的List(saveDataEntityList)中。

    伪代码如下:

    1. saveDataEntityList = entityList.stream().filter(f -> !exitList.stream().map(Entity::getId).collect(Collectors.toList()).contains(f.getId())
    2. ).collect(Collectors.toList());

    经过测试,发现OK,没有重复数据了。

    总结

    removeAll适合子集完全匹配和基础类型的操作,建议在自定义对象的时候,不要使用removeAll方法,而是使用stream的方式。

  • 相关阅读:
    Linux基础教程: 4、用户组和用户的创建
    Java中JVM的xmx和xms配置成一样的好处
    win11系统下,迅雷启动后闪退的问题
    C#基础:WPF中常见控件的布局基础
    相机-景深
    毫秒时间戳转换为字符串
    vue3使用v-model控制子组件进行双向数据绑定
    【依赖dependency / 插件Plugin】 not found?可以看看这思路
    Etcd 学习 安装教程
    “5G+北斗”赋能千行百业,中海达亮相2023中国移动全球合作伙伴大会
  • 原文地址:https://blog.csdn.net/m290345792/article/details/126030021