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

时间:2014-10-30 12:49:35

标签: java

我有两个Long类型的集合。两者的规模 20-30 。从第一个中删除的最快方法是什么?所占用的堆空间越小越好,因为还有其他事情并行进行。

我知道使用Iterator进行删除时LinkedList优于ArrayList,但我不确定是否需要迭代每个元素。我想轮询任何更好的方法,Collections都已排序。

编辑:我之前说过我的收藏尺寸为2-3百万,我意识到它 20-30 百万。 会有很多重叠。收藏的确切类型也有争议。

4 个答案:

答案 0 :(得分:1)

如果数量在数百万的范围内,那么O(n 2 )复杂度的解决方案就应该出来了。这里有两个基本解决方案:

  • 对第二个集合进行排序,并使用二进制搜索来寻找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();
    }
}

请注意,如果collection1ArrayList,则会非常慢。如果你必须保持ArrayList,你可以这样做:

int rd = 0, wr = 0;
// Copy the elements you are keeping into a contiguous range
while (rd != arrayList1.size()) {
    Long last = arrayList1.get(rd++);
    if (!toRemove.contains(iter.next()) {
        arrayList1.put(wr++, last);
    }
}
// Remove "tail" elements
while (rd > wr) {
    arrayList1.remove(--wr);
}

答案 1 :(得分:0)

没有增长堆。

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));
}
根据Oracles Javadoc,

b.size()b.get(int i)在不变的时间内运行。 a.remove(O o)也会在恒定时间内运行。

答案 2 :(得分:0)

第一个停靠点是Collection.removeAll方法。这不使用额外的堆空间,其时间复杂度取决于第二个集合上contains方法的性能。如果你的第二个集合是TreeSet,那么a.removeAll(b)需要O(n . log(m))时间(其中n是a的大小,m是b的大小),如果b是HashSet则需要O(n) time,如果b是一个已排序的ArrayList,那么它是O(nm),但您可以创建一个新的包装器Collection,它使用二进制搜索将其减少到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));
}

答案 3 :(得分:0)

你应该看看Apache Common Collections

我用包含~3M Longs的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));

我无法确保这是最好的解决方案,但它也很简单。

我的执行时间等于:

1247 ms

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