在Java中从另一个long集合中删除long集合的最快方法

在Java中从另一个long集合中删除long集合的最快方法,java,Java,我有两个Long类型的集合。规模均20-30100万。什么是最快的方法来消除一个,那些在第二个是常见的?占用的堆空间越小越好,因为还有其他事情在并行进行 我知道LinkedList比ArrayList更适合使用迭代器进行删除,但我不确定是否需要遍历每个元素。我想调查是否有更好的方法,这两个集合都已排序 编辑:我之前说过我的收藏规模是200-300万,但我意识到它是2000-30万。 将会有很多重叠。收集的确切类型也有争议。不增加堆 Collection<Long> a = new H

我有两个
Long
类型的集合。规模均20-30100万。什么是最快的方法来消除一个,那些在第二个是常见的?占用的堆空间越小越好,因为还有其他事情在并行进行

我知道
LinkedList
ArrayList
更适合使用迭代器进行删除,但我不确定是否需要遍历每个元素。我想调查是否有更好的方法,这两个
集合都已排序

编辑:我之前说过我的收藏规模是200-300万,但我意识到它是2000-30万。
将会有很多重叠。收集的确切类型也有争议。

不增加堆

Collection<Long> a = new HashSet<Long>();
//fill a
Collection<Long> b = new ArrayList<Long>();
//fill b
for(int i = 0; i < b.size(); i++){
    a.remove(b.get(i));
}
Collection a=newhashset();
//填补
集合b=新的ArrayList();
//填充b
对于(int i=0;i
b.size()
b.get(inti)
根据Oracles Javadoc以固定时间运行。
另外,
a.remove(O O)
以恒定时间运行。

当计数在数百万范围内时,应排除O(n2)复杂度的解决方案。这里有两个基本解决方案:

  • 对第二个集合进行排序,并对O((N+M)*logM)解决方案使用二进制搜索,或
  • 对于O(N+M)解决方案,将第二个集合中的元素放入哈希容器
上面,N是第一个集合中的元素数,M是第二个集合中的元素数

Set<Long> toRemove = new HashSet<Long>(collection2);
Iterator<Long> iter = collection1.iterator();
while (iter.hasNext()) {
    if (toRemove.contains(iter.next())) {
        iter.remove();
    }
}

第一个调用端口是方法。这不使用额外的堆空间,其时间复杂度取决于第二个集合上的
contains
方法的性能。如果第二个集合是树集,则
a.removeAll(b)
需要
O(n.log(m))
时间(其中n是a的大小,m是b的大小),如果b是散列集,则需要
O(n)
时间,如果b是排序的数组列表,则需要
O(nm)
,但您可以创建一个新的包装器集合,该集合使用二进制搜索将其减少到
O(n.log(m))
,而恒定内存成本可以忽略不计:

private static class SortedList<T extends Comparable<? super T>> extends com.google.common.collect.ForwardingList<T>
{

    private List delegate;

    public SortedList(ArrayList<T> delegate)
    {
        this.delegate = delegate;
    }

    @Override
    protected List<T> delegate()
    {
        return delegate;
    }

    @Override
    public boolean contains(Object object)
    {
        return Collections.binarySearch(delegate, (T) object) >= 0;
    }
}

static <E extends Comparable<? super E>> void removeAll(Collection<E> a, ArrayList<E> b)
{
    //assumes that b is sorted
    a.removeAll(new SortedList<E>(b));
}

private static class SortedList您应该看看

我使用长度约为3M的LinkedList进行了测试,结果非常好:

    Random r = new Random();
    List<Long> list1 = new LinkedList<Long>();
    for (int i = 0; i < 3000000; i++) {
        list1.add(r.nextLong());
    }
    List<Long> list2 = new LinkedList<Long>();
    for (int i = 0; i < 2000000; i++) {
        list2.add(r.nextLong());
    }

    Collections.sort(list1);
    Collections.sort(list2);

    long time = System.currentTimeMillis();
    list3 = ListUtils.subtract(list2, list1);
    System.out.println("listUtils.intersection = " + (System.currentTimeMillis() - time));

不方便:它会创建一个新列表

如果它们已排序,您可以使用普通的二进制查找来查找要从第二个列表中删除的元素。显然,您需要迭代要删除的数字集合。在该大小下,集合的确切类型非常重要。re:集合的确切类型&heapspace:例如,为不使用大量包装器对象的基本类型提供“集合”实现。节省大量堆空间,速度方面不知道。预计会有多少重叠?如果不是很多,您可以为第二个集合构建Bloom过滤器,并使用它排除需要删除的元素。如果第二个集合是常量(或从未删除元素),则更好。@bizclop预期会有很多重叠。我正在寻找比删除所有ArrayList性能更好的集合。我的收藏规模相对较大。
    Random r = new Random();
    List<Long> list1 = new LinkedList<Long>();
    for (int i = 0; i < 3000000; i++) {
        list1.add(r.nextLong());
    }
    List<Long> list2 = new LinkedList<Long>();
    for (int i = 0; i < 2000000; i++) {
        list2.add(r.nextLong());
    }

    Collections.sort(list1);
    Collections.sort(list2);

    long time = System.currentTimeMillis();
    list3 = ListUtils.subtract(list2, list1);
    System.out.println("listUtils.intersection = " + (System.currentTimeMillis() - time));
1247 ms