从逻辑上讲,以下内容完全相同:
var foo = ( A.Union(B).Union(C).Union(D) ).ToList();
var bar = ( A.Union(B.Union(C.Union(D))) ).ToList();
var baz = ( D.Union(C.Union(B.Union(A))) ).ToList();
他们应该在最后返回完全相同的列表。
它们之间有什么区别(如果有的话)?
我猜想唯一的区别是与您在每个集合中迭代的频率相关的性能问题?那么foo
和baz
具有完全相同的效果 - 迭代A
4次,但仅超过D
一次?
是吗?
是否有任何其他有趣的属性可能会引导您关注做一个而不是另一个?
答案 0 :(得分:5)
这些解决方案都没有多次迭代它的参数。此外,参数按照文本中给出的顺序进行迭代,A
,B
,C
,D
用于foo
和{{1} } {},bar
,D
,C
,B
A
。
您可以使用一个简单的生成器来演示这一点,该生成器会在您迭代时打印它返回的项目:
baz
多次枚举集合的原因是class VisibleIterator : IEnumerable<string> {
private readonly string name;
public VisibleIterator(string name) {
this.name = name;
}
public IEnumerator<string> GetEnumerator() {
for (var i = 0 ; i != 4 ; i++) {
var res = name+i;
Console.WriteLine(res);
yield return res;
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
({1}}后面的代码保留了已访问过的项的哈希集:
UnionIterator<T>
Union<T>
内static IEnumerable<TSource> UnionIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer) {
Set<TSource> set = new Set<TSource>(comparer);
foreach (TSource element in first)
if (set.Add(element)) yield return element;
foreach (TSource element in second)
if (set.Add(element)) yield return element;
}
的大小可能会导致性能差异很小。每个示例中都会有三个这样的集合 - 每个Set<TSource> set
调用一个。顶级UnionIterator<T>
始终以Union
结果的所有成员结束。但是,中级set
可能包含更多项目或更少项目,具体取决于您合并馆藏的顺序以及每个馆藏中相对项目的数量。
答案 1 :(得分:1)
虽然dasblinkenlight是正确的,每个项目都只迭代一次,但三个版本可能仍然具有可衡量的性能差异,具体取决于您的对象。
这些项目将被插入到不同数量的Hashsets中,具体取决于它们在Union树下的距离。
虽然插入Hashset名义上是O(1)
,但它确实有成本,而且在实践中并不总是不变的,具体取决于对象的细节。
当项目被插入Hashset时,会调用GetHashCode
,并且需要使用Equals
将项目与集合中具有相同int哈希码的任何其他对象进行比较。对于极其复杂的对象,GetHashCode
可能很昂贵。如果项目hashkeys没有广泛分发,则可能会调用Equals
,这可能很昂贵。
以下演示基于@dasblinkenlight的回答显示GetHashCode
被调用的次数不同,具体取决于Union
排序。在哈希冲突的情况下,我没有演示Equals
被调用,但如果你愿意,你可以尝试一下。
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
public class Test {
public static void Main() {
var A = new VisibleIterator("A");
var B = new VisibleIterator("B");
var C = new VisibleIterator("C");
var D = new VisibleIterator("D");
Console.WriteLine("--- A.Union(B).Union(C).Union(D)");
var foo = (A.Union(B).Union(C).Union(D)).ToList();
Console.WriteLine("--- A.Union(B.Union(C.Union(D)))");
var bar = (A.Union(B.Union(C.Union(D)))).ToList();
Console.WriteLine("--- D.Union(C.Union(B.Union(A)))");
var baz = (D.Union(C.Union(B.Union(A)))).ToList();
}
}
class VisibleIterator : IEnumerable<VisibleHasher> {
private readonly string name;
public VisibleIterator(string name) {
this.name = name;
}
public IEnumerator<VisibleHasher> GetEnumerator() {
for (var i = 0 ; i != 4 ; i++) {
var res = name+i;
Console.WriteLine("Iterating " + res);
yield return new VisibleHasher(res);
}
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
class VisibleHasher {
private readonly string val;
public VisibleHasher(String val) {
this.val = val;
}
public override int GetHashCode() {
Console.WriteLine("Hashing '" + val + "'");
return val.GetHashCode();
}
}
Demo(基于dasblinkenlight&#39;答案)
如果您认为这些哈希插入的成本可能很高,那么以下内容应该保证每个项目有一个哈希插入:
A.Concat(B).Concat(C).Concat(D).Distinct().ToList()