找出两个排序列表是否包含相同元素 Java 的有效方法。
Efficient way to find out if two sorted lists contain same element Java.
我有一个搜索互素数的紧密循环。一个列表 primeFactors
。它的第 n 个元素包含 n 的质数分解的排序列表。我正在使用 checkIfPrimes
检查 c
和 d
是否互质
boolean checkIfPrimes(int c, int d, List<List<Integer>> primeFactors) {
List<Integer> common = new ArrayList<>(primeFactors.get(d)); //slow
common.retainAll(primeFactors.get(c));
return (common.isEmpty());
}
primeFactors.get(d).retainAll(primeFactors.get(c))
看起来很有希望,但它会改变我的可重用 primeFactors
对象。
创建新对象相对较慢。有没有办法加快这一步?我能以某种方式利用列表已排序的事实吗?我应该改用数组吗?
通常您可以使用布尔数组。其中数组的索引是布尔值 returns true
的数字和值,否则是 false
.
您可以使用 Collection
进行更快的查找 - 例如Set
如果您只需要不重复的质因数,或者 Map
如果您还需要每个因子的计数。
基本上,你想知道两个Set的交集是否为空。 Oracle Set tutorial 显示了一种计算交集的方法(类似于您已经提到的,在副本上使用 retainAll
,但在集合上操作应该更有效)。
您可以按照以下方式做一些事情:
List<Integer> commonElements =
primeFactors.get(d).stream()
.filter(primeFactors.get(c)::contains)
.collect(Collectors.toList());
衡量此性能后,您可以用 'parallelStream()' 替换上面的 'stream()',看看您获得了哪些好处。
集合操作应该比数组操作更快。
只是为了好玩,考虑尝试一下并将性能与流性能进行比较:
final Set<Integer> commonSet;
final Set<Integer> cSet = new HashSet<Integer>();
final Set<Integer> dSet = new HashSet<Integer>();
cSet.addAll(primeFactors.get(c));
dSet.addAll(primeFactors.get(d));
commonSet = dSet.retainAll(cSet);
return (commonSet.isEmpty());
此外,
考虑使用 List<Set<Integer>> primeFactors
而不是 List<List<Integer>> primeFactors
因为我怀疑你不
确实有一个主要因素列表,但实际上有一组主要因素。
由于您的列表相对较小,并且此操作经常执行,因此您应避免创建任何新的列表或集合,因为这可能会导致很大的 GC 压力。
扫描线性算法为
public static boolean emptyIntersection(List<Integer> sortedA, List<Integer> sortedB) {
if (sortedA.isEmpty() || sortedB.isEmpty())
return true;
int sizeA = sortedA.size(), sizeB = sortedB.size();
int indexA = 0, indexB = 0;
int elementA = sortedA.get(indexA), elementB = sortedB.get(indexB);
while (true) {
if (elementA == elementB) {
return false;
} else if (elementA < elementB) {
indexA++;
if (indexA == sizeA)
return true;
elementA = sortedA.get(indexA);
} else {
// elementB < elementA
indexB++;
if (indexB == sizeB)
return true;
elementB = sortedB.get(indexB);
}
}
}
还可以考虑使用原始 int
列表而不是盒装整数,例如。 G。来自 fastutil 图书馆。
我有一个搜索互素数的紧密循环。一个列表 primeFactors
。它的第 n 个元素包含 n 的质数分解的排序列表。我正在使用 checkIfPrimes
c
和 d
是否互质
boolean checkIfPrimes(int c, int d, List<List<Integer>> primeFactors) {
List<Integer> common = new ArrayList<>(primeFactors.get(d)); //slow
common.retainAll(primeFactors.get(c));
return (common.isEmpty());
}
primeFactors.get(d).retainAll(primeFactors.get(c))
看起来很有希望,但它会改变我的可重用 primeFactors
对象。
创建新对象相对较慢。有没有办法加快这一步?我能以某种方式利用列表已排序的事实吗?我应该改用数组吗?
通常您可以使用布尔数组。其中数组的索引是布尔值 returns true
的数字和值,否则是 false
.
您可以使用 Collection
进行更快的查找 - 例如Set
如果您只需要不重复的质因数,或者 Map
如果您还需要每个因子的计数。
基本上,你想知道两个Set的交集是否为空。 Oracle Set tutorial 显示了一种计算交集的方法(类似于您已经提到的,在副本上使用 retainAll
,但在集合上操作应该更有效)。
您可以按照以下方式做一些事情:
List<Integer> commonElements =
primeFactors.get(d).stream()
.filter(primeFactors.get(c)::contains)
.collect(Collectors.toList());
衡量此性能后,您可以用 'parallelStream()' 替换上面的 'stream()',看看您获得了哪些好处。
集合操作应该比数组操作更快。 只是为了好玩,考虑尝试一下并将性能与流性能进行比较:
final Set<Integer> commonSet;
final Set<Integer> cSet = new HashSet<Integer>();
final Set<Integer> dSet = new HashSet<Integer>();
cSet.addAll(primeFactors.get(c));
dSet.addAll(primeFactors.get(d));
commonSet = dSet.retainAll(cSet);
return (commonSet.isEmpty());
此外,
考虑使用 List<Set<Integer>> primeFactors
而不是 List<List<Integer>> primeFactors
因为我怀疑你不
确实有一个主要因素列表,但实际上有一组主要因素。
由于您的列表相对较小,并且此操作经常执行,因此您应避免创建任何新的列表或集合,因为这可能会导致很大的 GC 压力。
扫描线性算法为
public static boolean emptyIntersection(List<Integer> sortedA, List<Integer> sortedB) {
if (sortedA.isEmpty() || sortedB.isEmpty())
return true;
int sizeA = sortedA.size(), sizeB = sortedB.size();
int indexA = 0, indexB = 0;
int elementA = sortedA.get(indexA), elementB = sortedB.get(indexB);
while (true) {
if (elementA == elementB) {
return false;
} else if (elementA < elementB) {
indexA++;
if (indexA == sizeA)
return true;
elementA = sortedA.get(indexA);
} else {
// elementB < elementA
indexB++;
if (indexB == sizeB)
return true;
elementB = sortedB.get(indexB);
}
}
}
还可以考虑使用原始 int
列表而不是盒装整数,例如。 G。来自 fastutil 图书馆。