虽然 Jon Skeet 的回答对于日常使用少量到中等数量的元素(最多几百万)的练习来说是一个很好的建议,但它并不是最快的方法,也不是非常有效的资源。一个明显的缺点是,要获得完整的差异,需要对数据进行两次传递(如果相同的元素也很感兴趣,即使是 3 次)。显然,可以通过自定义重新实现该Except
方法来避免这种情况,但仍然存在哈希集的创建需要大量内存并且哈希的计算需要时间。
对于非常大的数据集(数十亿个元素),考虑特定情况通常是值得的。以下是一些可能会提供一些启发的想法: 如果可以比较元素(在实践中几乎总是如此),那么对列表进行排序并应用以下 zip 方法是值得考虑的:
/// <returns>The elements of the specified (ascendingly) sorted enumerations that are
/// contained only in one of them, together with an indicator,
/// whether the element is contained in the reference enumeration (-1)
/// or in the difference enumeration (+1).</returns>
public static IEnumerable<Tuple<T, int>> FindDifferences<T>(IEnumerable<T> sortedReferenceObjects,
IEnumerable<T> sortedDifferenceObjects, IComparer<T> comparer)
{
var refs = sortedReferenceObjects.GetEnumerator();
var diffs = sortedDifferenceObjects.GetEnumerator();
bool hasNext = refs.MoveNext() && diffs.MoveNext();
while (hasNext)
{
int comparison = comparer.Compare(refs.Current, diffs.Current);
if (comparison == 0)
{
// insert code that emits the current element if equal elements should be kept
hasNext = refs.MoveNext() && diffs.MoveNext();
}
else if (comparison < 0)
{
yield return Tuple.Create(refs.Current, -1);
hasNext = refs.MoveNext();
}
else
{
yield return Tuple.Create(diffs.Current, 1);
hasNext = diffs.MoveNext();
}
}
}
例如,这可以通过以下方式使用:
const int N = <Large number>;
const int omit1 = 231567;
const int omit2 = 589932;
IEnumerable<int> numberSequence1 = Enumerable.Range(0, N).Select(i => i < omit1 ? i : i + 1);
IEnumerable<int> numberSequence2 = Enumerable.Range(0, N).Select(i => i < omit2 ? i : i + 1);
var numberDiffs = FindDifferences(numberSequence1, numberSequence2, Comparer<int>.Default);
在我的计算机上进行基准测试给出了 N = 1M 的以下结果:
方法 |
意思是 |
错误 |
标准差 |
比率 |
0代 |
第一代 |
第 2 代 |
已分配 |
DiffLinq |
115.19 毫秒 |
0.656 毫秒 |
0.582 毫秒 |
1.00 |
2800.0000 |
2800.0000 |
2800.0000 |
67110744乙 |
区分邮编 |
23.48 毫秒 |
0.018 毫秒 |
0.015 毫秒 |
0.20 |
- |
- |
- |
720乙 |
对于 N = 100M:
方法 |
意思是 |
错误 |
标准差 |
比率 |
0代 |
第一代 |
第 2 代 |
已分配 |
DiffLinq |
12.146 秒 |
0.0427 秒 |
0.0379 秒 |
1.00 |
13000.0000 |
13000.0000 |
13000.0000 |
8589937032 乙 |
区分邮编 |
2.324 秒 |
0.0019 秒 |
0.0018 秒 |
0.19 |
- |
- |
- |
720乙 |
请注意,此示例当然受益于列表已经排序并且可以非常有效地比较整数这一事实。但这正是重点:如果您确实有有利的环境,请确保利用它们。
一些进一步的评论:比较功能的速度显然与整体性能相关,因此对其进行优化可能是有益的。这样做的灵活性是压缩方法的一个好处。此外,并行化对我来说似乎更可行,尽管绝不容易,而且可能不值得付出努力和开销。然而,将过程加速大约 2 倍的简单方法是将列表分别分成两半(如果可以有效地完成)并并行比较各个部分,一个从前到后处理,另一个处理以相反的顺序。