我有两个Long
类型的集合。两者的规模 20-30 。从第一个中删除的最快方法是什么?所占用的堆空间越小越好,因为还有其他事情并行进行。
我知道使用Iterator进行删除时LinkedList
优于ArrayList
,但我不确定是否需要迭代每个元素。我想轮询任何更好的方法,Collections
都已排序。
编辑:我之前说过我的收藏尺寸为2-3百万,我意识到它 20-30 百万。 会有很多重叠。收藏的确切类型也有争议。
答案 0 :(得分:1)
如果数量在数百万的范围内,那么O(n 2 )复杂度的解决方案就应该出来了。这里有两个基本解决方案:
上面,N是第一个集合中元素的数量,M是第二个集合中元素的数量。
Set<Long> toRemove = new HashSet<Long>(collection2);
Iterator<Long> iter = collection1.iterator();
while (iter.hasNext()) {
if (toRemove.contains(iter.next())) {
iter.remove();
}
}
请注意,如果collection1
是ArrayList
,则会非常慢。如果你必须保持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
不方便:它会创建一个新列表