在Java中高效地计算两个集合的交集?
在Java中,找到两个非稀疏集的交集大小的最有效方法是什么?这是一个操作,我将在大型设备上多次调用,因此优化非常重要。我无法修改原始集 我已经看过Apache Commons CollectionUtils.intersection,它看起来相当慢。我目前的方法是取两个集合中较小的集合,克隆它,然后在两个集合中较大的集合上调用.retainal在Java中高效地计算两个集合的交集?,java,performance,optimization,set,Java,Performance,Optimization,Set,在Java中,找到两个非稀疏集的交集大小的最有效方法是什么?这是一个操作,我将在大型设备上多次调用,因此优化非常重要。我无法修改原始集 我已经看过Apache Commons CollectionUtils.intersection,它看起来相当慢。我目前的方法是取两个集合中较小的集合,克隆它,然后在两个集合中较大的集合上调用.retainal public static int getIntersection(Set<Long> set1, Set<Long> set2
public static int getIntersection(Set<Long> set1, Set<Long> set2) {
boolean set1IsLarger = set1.size() > set2.size();
Set<Long> cloneSet = new HashSet<Long>(set1IsLarger ? set2 : set1);
cloneSet.retainAll(set1IsLarger ? set1 : set2);
return cloneSet.size();
}
publicstaticintgetcrossion(Set set1,Set set2){
布尔值set1IsLarger=set1.size()>set2.size();
Set cloneSet=newhashset(set1IsLarger?set2:set1);
复盖cloneSet.Retainal(Set1Slarger?set1:set2);
返回cloneSet.size();
}
集合的成员是否可以轻松映射到相对较小的整数范围?如果是,考虑使用位集。然后,交集是按位的,一次是s-32个潜在成员。只需使用的方法。这是一个很好的方法。您应该从当前解决方案中获得O(n)性能 使用发布的方法运行一些测试,而不是构建一个新的HashSet。也就是说,让A
为较小的集合,B
为较大的集合,然后,对于A
中的每个项目,如果它也存在于B中,则将其添加到C(一个新的哈希集)——仅为了计数,可以跳过中间的C集合
正如发布的方法一样,这应该是成本上的O(| a |)
,因为迭代是O(| a |)
,而对B的探索是O(1)
。我不知道它将如何与克隆和删除方法进行比较
快乐编码——并发布一些结果;-)
事实上,进一步思考,我相信这比文章中的方法有更好的界限:
O(| A |)
vsO(| A |+| B |)
。我不知道这是否会在现实中产生任何差异(或改进),我只希望当|A |两个集合都可以排序时,它才是相关的,比如TreeSet
运行两个迭代器可以更快地计算共享对象的数量
如果您经常执行此操作,如果您可以包装集合,以便缓存交叉点操作的结果,保留dirty
标记以跟踪缓存结果的有效性,并在需要时重新计算,则可能会带来很多好处。您可以使用Set方法retainal()避免所有手动工作
从文档:
s1.Retainal(s2)-将s1转换为s1和s2的交点。(两个集合的交集是仅包含两个集合共有的元素的集合。)
仅供参考,如果任何集合都使用相同的比较关系进行排序,则可以在时间N*M中迭代它们的交集,其中N是最小集合的大小,M是集合的数量
实现留给读者作为练习 使用Java 8流:
set1.stream().filter(s -> set2.contains(s)).collect(Collectors.toList());
如果计算交集只是为了计算集合中有多少元素,我建议您只需要直接计算交集,而不是构建集合,然后调用size()
我的计数功能:
/**
* Computes the size of intersection of two sets
* @param small first set. preferably smaller than the second argument
* @param large second set;
* @param <T> the type
* @return size of intersection of sets
*/
public <T> int countIntersection(Set<T> small, Set<T> large){
//assuming first argument to be smaller than the later;
//however double checking to be sure
if (small.size() > large.size()) {
//swap the references;
Set<T> tmp = small;
small = large;
large = tmp;
}
int result = 0;
for (T item : small) {
if (large.contains(item)){
//item found in both the sets
result++;
}
}
return result;
}
/**
*计算两个集合的交集的大小
*@param small第一套。最好小于第二个参数
*@param大第二套;
*@param类型
*@集合交集的返回大小
*/
公共int计数交叉口(设置为小,设置为大){
//假设第一个参数小于后一个参数;
//不过,请仔细检查以确保
if(small.size()>large.size()){
//交换参考文献;
设置tmp=小;
小=大;
大=tmp;
}
int结果=0;
用于(T项目:小型){
if(大型包含(项目)){
//在两个集合中都找到了项
结果++;
}
}
返回结果;
}
通过流计算交叉点/reduce(它假设您在调用之前先计算出哪个集合更大):
public int countIntersect(设置大集合,设置小集合){
返回smallerSet.stream().reduce(0,(a,b)->largerSet.contains(b)→a+1:a);
}
然而,我在其他地方读到,没有任何java代码能够比Set操作的Set方法更快,因为它们是作为本机代码而不是java代码实现的。因此,我支持尝试BitSet以获得更快的结果的建议。+1同意,尽管在幕后,它几乎与OP的方法一样,尽管没有复制。你知道这一方法的效率吗?@Ina它是开源的,所以你可以自己看看:哦,谷歌。有多少问题以“做X最有效的方法是什么”开始,以Google Guava结束?据我所知,CollectionUtils.Intersection是一种更通用的方法(也可以应用于列表),这就是为什么它不适用于集合。您应该检查:布尔值的size()
是多少?:-)如果用一个if语句代替三个?:
语句,这可能会稍微快一点(极端微观优化)。这样,它只需要分支一次(可能很昂贵),而不是三次。我用这两种方法进行了测试,没有发现任何区别——也许编译器或运行时正在为我处理这一问题。如果没有任何信息,我们谈论的是哪种类型的集合,这个问题很难明确回答。不同的场景对于不同的动作有不同的成本。这取决于我们谈论的场景类型。您的复杂性似乎假设了一个哈希集,在这种情况下,我同意我们不能比that@Voo是的,我确实假设了一个哈希集——很好的调用。(以上所有帖子都假设一个哈希集。)感谢您做了所有这些分析。但是为了改进你的答案,你真的能发布在你的基准测试中获胜的方法吗(例如MyMethod1),这将使人们不必阅读thr
set1.stream().filter(s -> set2.contains(s)).collect(Collectors.toList());
/**
* Computes the size of intersection of two sets
* @param small first set. preferably smaller than the second argument
* @param large second set;
* @param <T> the type
* @return size of intersection of sets
*/
public <T> int countIntersection(Set<T> small, Set<T> large){
//assuming first argument to be smaller than the later;
//however double checking to be sure
if (small.size() > large.size()) {
//swap the references;
Set<T> tmp = small;
small = large;
large = tmp;
}
int result = 0;
for (T item : small) {
if (large.contains(item)){
//item found in both the sets
result++;
}
}
return result;
}
public int countIntersect(Set<Integer> largerSet, Set<Integer> smallerSet){
return smallerSet.stream().reduce(0, (a,b) -> largerSet.contains(b)?a+1:a);
}