背景
项目中使用到List求交集,很容易想到collecion.retainAll()方法,但是在数据量比较大时,这个方法效率并不高。本文研究了几种常用的方法,以供大家参考。
方法
【首先】 梳理下思路,List去重一般有几种方法。
- 『外层遍历+内层遍历』查找:
复杂度O(NM) ,一般使用contains()检查是否包含
- 『外层遍历+内层Hash』查找:
复杂度O(N),一般将内层List转化为HashSet实现
- 『外层遍历+内层bitMap』查找:
复杂度O(N),一般将内层List转化为字节映射实现
【其次】 这里其实忽略了一个点,就是 『单层遍历』中,检查 元素不包含 时,需要将这个 元素移除 (即remove方法)。remove时,也会导致性能问题。
这里面我们使用Java8中 java.util.AbstractCollection#retainAll 方法来验证下我们的思路。
// Java8 中 方法:java.util.AbstractCollection#retainAll
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<E> it = iterator();
// 1. 外层遍历
while (it.hasNext()) {
// 2. 内层查找『是否包含』
if (!c.contains(it.next())) {
// 3. 不包含时,移除外层元素
it.remove();
modified = true;
}
}
return modified;
}
这里用图总结下,求交集的流程:

实现
1.『外层遍历+内层遍历』查找:
java中常用2种遍历查找的List:ArrayList、LinkedList,在内外层中测试。
// 外层:ArrayList,内层:ArrayList
private void outArrayListInnerArrayList(List<Long> listA, List<Long> listB) {
long begin = System.currentTimeMillis();
ArrayList<Long> setA = new ArrayList<>(listA);
ArrayList<Long> setB = new ArrayList<>(listB);
setA.retainAll(setB);
long end = System.currentTimeMillis();
System.out.println("[ArrayList-ArrayList]RetainAll耗时:" + (end - begin));
}
// 外层:LinkedList,内层:ArrayList
private void outLinkedListInnerArrayList(List<Long> listA, List<Long> listB) {
long begin = System.currentTimeMillis();
LinkedList<Long> setA = new LinkedList<>(listA);
ArrayList<Long> setB = new ArrayList<>(listB);
setA.retainAll(setB);
long end = System.currentTimeMillis();
System.out.println("[LinkedList-ArrayList]RetainAll耗时:" + (end - begin));
}
// 外层:ArrayList,内层:LinkedList
private void outArrayListInnerLinkedList(List<Long> listA, List<Long> listB) {
long begin = System.currentTimeMillis();
LinkedList<Long> setA = new LinkedList<>(listA);
ArrayList<Long> setB = new ArrayList<>(listB);
setA.retainAll(setB);
long end = System.currentTimeMillis();
System.out.println("[LinkedList-ArrayList]RetainAll耗时:" + (end - begin));
}
// 外层:LinkedList,内层:LinkedList
private void outLinkedListInnerLinkedList(List<Long> listA, List<Long> listB) {
long begin = System.currentTimeMillis();
LinkedList<Long> setA = new LinkedList<>(listA);
ArrayList<Long> setB = new ArrayList<>(listB);
setA.retainAll(setB);
long end = System.currentTimeMillis();
System.out.println("[LinkedList-LinkedList]RetainAll耗时:" + (end - begin));
}
2.『外层遍历+内层Hash』查找:
java中常用HashSet,内层替换为HashSet查找。
// 外层:ArrayList,内层:HashSet
private void outArrayListInnerHashSet(List<Long> listA, List<Long> listB) {
long begin = System.currentTimeMillis();
ArrayLi

本文探讨了在Java中处理大量Long集合时,求交集的三种方法:外层遍历加内层遍历、外层遍历加内层HashSet以及外层遍历加内层RoaringBitmap,并通过测试比较了它们的性能。RoaringBitmap在某些场景下能提供更好的性能和压缩效果,被广泛应用于Apache等开源项目。

1956

被折叠的 条评论
为什么被折叠?



