List获取差集产生的问题

Posted 沛沛老爹

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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源码牌子。

public boolean removeAll(Collection<?> c) 
        return batchRemove(c, false, 0, size);

...

boolean batchRemove(Collection<?> c, boolean complement,
                        final int from, final int end) 
        Objects.requireNonNull(c);
        final Object[] es = elementData;
        int r;
        // Optimize for initial run of survivors
        for (r = from;; r++) 
            if (r == end)
                return false;
            if (c.contains(es[r]) != complement)
                break;
        
        int w = r++;
        try 
            for (Object e; r < end; r++)
                if (c.contains(e = es[r]) == complement)
                    es[w++] = e;
         catch (Throwable ex) 
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            System.arraycopy(es, r, es, w, end - r);
            w += end - r;
            throw ex;
         finally 
            modCount += end - w;
            shiftTailOverGap(es, w, end);
        
        return true;
    

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

 for (r = from;; r++) 
            if (r == end)
                return false;
            if (c.contains(es[r]) != complement)
                break;
        

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

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

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

伪代码如下:

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

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

总结

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

以上是关于List获取差集产生的问题的主要内容,如果未能解决你的问题,请参考以下文章

C# Linq获取两个List或数组的差集交集

列表交集,并集和差集

Java使用不同方式获取两个集合List的交集补集并集(相加)差集(相减)

求2个list的差集

两个list求差集

list的取差集