Performance Linq连接两个列表:使用字典是否更有效?
最后的重新措辞 下面我连接了两个序列,我想知道使用连接的keySelector作为键创建一个序列的字典,并遍历另一个集合并在字典中找到键是否会更快 这仅在键选择器唯一时有效。如果两个记录具有相同的密钥,则真正的联接没有问题。在字典中,你必须有唯一的键 我测量了差异,发现dictionary方法快了13%。在大多数用例中是可忽略的。看到我对这个问题的回答了吗 重新措辞的问题 有人建议这个问题与相同,但这个问题不是关于使用where或join,而是关于使用字典执行join 我的问题是:如果我想基于一个键选择器连接两个序列,哪种方法会更快Performance Linq连接两个列表:使用字典是否更有效?,performance,linq,Performance,Linq,最后的重新措辞 下面我连接了两个序列,我想知道使用连接的keySelector作为键创建一个序列的字典,并遍历另一个集合并在字典中找到键是否会更快 这仅在键选择器唯一时有效。如果两个记录具有相同的密钥,则真正的联接没有问题。在字典中,你必须有唯一的键 我测量了差异,发现dictionary方法快了13%。在大多数用例中是可忽略的。看到我对这个问题的回答了吗 重新措辞的问题 有人建议这个问题与相同,但这个问题不是关于使用where或join,而是关于使用字典执行join 我的问题是:如果我想基于一
带示例的原始问题 我有两个类,都有一个属性引用。我有两个这些类的序列,我想基于相等的引用将它们连接起来
Class ClassA
{
public string Reference {get;}
...
}
public ClassB
{
public string Reference {get;}
...
}
var listA = new List<ClassA>()
{
new ClassA() {Reference = 1, ...},
new ClassA() {Reference = 2, ...},
new ClassA() {Reference = 3, ...},
new ClassA() {Reference = 4, ...},
}
var listB = new List<ClassB>()
{
new ClassB() {Reference = 1, ...},
new ClassB() {Reference = 3, ...},
new ClassB() {Reference = 5, ...},
new ClassB() {Reference = 7, ...},
}
我不确定这是如何工作的,但我可以想象,对于listA中的每个a,listB都会被迭代,以查看listB中是否有一个b具有与a相同的引用
问题:如果我知道引用是不同的,那么将B转换成字典并比较listA中每个元素的引用不是更有效吗
var dictB = listB.ToDictionary<string, ClassB>()
var myJoin = listA
.Where(a => dictB.ContainsKey(a.Reference))
.Select(a => new (A = a, B = dictB[a.Reference]);
var dictB=listB.ToDictionary()
var myJoin=listA
.其中(a=>dictB.ContainsKey(a.Reference))
.选择(a=>new(a=a,B=dictB[a.Reference]);
这样,listB的每个元素必须访问一次才能放入字典,listA的每个元素必须访问一次,hascode引用必须计算一次
这种方法对于大型收集会更快吗?我为此创建了一个测试程序,并测量了所用的时间 假设我有一类Person,每个Person都有一个名字和一个Person类型的父属性。如果父属性不知道,则父属性为null 我有一系列杂种(没有父亲),他们只有一个儿子和一个女儿。所有的女儿都放在一个序列中。所有的儿子都放在另一个序列中 问题:加入有相同父亲的儿子和女儿 结果:使用Enumerable.Join加入100万个家庭需要1.169秒。使用Dictionary Join加入家庭需要1.024秒。速度稍微快了一点 守则:
class Person : IEquatable<Person>
{
public string Name { get; set; }
public Person Father { get; set; }
// + a lot of equality functions get hash code etc
// for those interested: see the bottom
}
const int nrOfBastards = 1000000; // one million
var bastards = Enumerable.Range (0, nrOfBastards)
.Select(i => new Person()
{ Name = 'B' + i.ToString(), Father = null })
.ToList();
var sons = bastards.Select(father => new Person()
{Name = "Son of " + father.Name, Father = father})
.ToList();
var daughters = bastards.Select(father => new Person()
{Name = "Daughter of " + father.Name, Father = father})
.ToList();
// join on same parent: Traditionally and using Dictionary
var stopwatch = Stopwatch.StartNew();
this.TraditionalJoin(sons, daughters);
var time = stopwatch.Elapsed;
Console.WriteLine("Traditional join of {0} sons and daughters took {1:F3} sec", nrOfBastards, time.TotalSeconds);
stopwatch.Restart();
this.DictionaryJoin(sons, daughters);
time = stopwatch.Elapsed;
Console.WriteLine("Dictionary join of {0} sons and daughters took {1:F3} sec", nrOfBastards, time.TotalSeconds);
}
private void TraditionalJoin(IEnumerable<Person> boys, IEnumerable<Person> girls)
{ // join on same parent
var family = boys
.Join(girls,
boy => boy.Father,
girl => girl.Father,
(boy, girl) => new { Son = boy.Name, Daughter = girl.Name })
.ToList();
}
private void DictionaryJoin(IEnumerable<Person> sons, IEnumerable<Person> daughters)
{
var sonsDictionary = sons.ToDictionary(son => son.Father);
var family = daughters
.Where(daughter => sonsDictionary.ContainsKey(daughter.Father))
.Select(daughter => new { Son = sonsDictionary[daughter.Father], Daughter = daughter })
.ToList();
}
班级人员:可胜任
{
公共字符串名称{get;set;}
公众人物父{get;set;}
//+许多相等函数获得哈希码等
//对于那些感兴趣的人:看下面
}
常量int nrofstards=1000000;//一百万
var bastards=可枚举的范围(0,nrOfBastards)
.Select(i=>newperson()
{Name='B'+i.ToString(),Father=null})
.ToList();
var sons=bastards.Select(父亲=>newperson()
{Name=“父亲的儿子”+父亲的名字,父亲=父亲})
.ToList();
var女儿=杂种。选择(父亲=>新人()
{Name=“父亲的女儿”+父亲的名字,父亲=父亲})
.ToList();
//在同一父节点上加入:传统和使用字典
var stopwatch=stopwatch.StartNew();
这是传统的结合(儿子、女儿);
var时间=秒表。已用时间;
Console.WriteLine(“传统的{0}子和子的连接需要{1:F3}秒”,nrofstards,time.TotalSeconds);
stopwatch.Restart();
本词典加入(儿子、女儿);
时间=秒表。已过;
Console.WriteLine(“字典连接{0}个儿子和女儿花了{1:F3}秒”,nrofstards,time.TotalSeconds);
}
private void Traditional Join(IEnumerable男孩、IEnumerable女孩)
{//在同一父节点上联接
家庭=男孩
.加入(女孩),
男孩=>男孩,父亲,
女孩=>女孩,父亲,
(男孩,女孩)=>新的{儿子=男孩。名字,女儿=女孩。名字})
.ToList();
}
私有void字典join(IEnumerable子,IEnumerable子)
{
var sonsDictionary=sons.ToDictionary(son=>son.Father);
家庭=女儿
.Where(女儿=>sonsDictionary.ContainsKey(女儿.父亲))
.Select(女儿=>new{Son=sonsDictionary[女儿.Father],女儿=女儿})
.ToList();
}
对于那些对人的平等感兴趣的人,需要一本合适的字典:
class Person : IEquatable<Person>
{
public string Name { get; set; }
public Person Father { get; set; }
public bool Equals(Person other)
{
if (other == null)
return false;
else if (Object.ReferenceEquals(this, other))
return true;
else if (this.GetType() != other.GetType())
return false;
else
return String.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase);
}
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public override int GetHashCode()
{
const int prime1 = 899811277;
const int prime2 = 472883293;
int hash = prime1;
unchecked
{
hash = hash * prime2 + this.Name.GetHashCode();
if (this.Father != null)
{
hash = hash * prime2 + this.Father.GetHashCode();
}
}
return hash;
}
public override string ToString()
{
return this.Name;
}
public static bool operator==(Person x, Person y)
{
if (Object.ReferenceEquals(x, null))
return Object.ReferenceEquals(y, null);
else
return x.Equals(y);
}
public static bool operator!=(Person x, Person y)
{
return !(x==y);
}
}
班级人员:可胜任
{
公共字符串名称{get;set;}
公众人物父{get;set;}
公共布尔等于(其他人)
{
如果(其他==null)
返回false;
else if(Object.ReferenceEquals(this,other))
返回true;
else if(this.GetType()!=other.GetType())
返回false;
其他的
返回String.Equals(this.Name,other.Name,StringComparison.OrdinalIgnoreCase);
}
公共覆盖布尔等于(对象对象对象)
{
返回此.Equals(对象为个人);
}
公共覆盖int GetHashCode()
{
常量int prime1=899811277;
常量int prime2=472883293;
int hash=prime1;
未经检查
{
hash=hash*prime2+this.Name.GetHashCode();
if(this.Father!=null)
{
hash=hash*prime2+this.Father.GetH
class Person : IEquatable<Person>
{
public string Name { get; set; }
public Person Father { get; set; }
public bool Equals(Person other)
{
if (other == null)
return false;
else if (Object.ReferenceEquals(this, other))
return true;
else if (this.GetType() != other.GetType())
return false;
else
return String.Equals(this.Name, other.Name, StringComparison.OrdinalIgnoreCase);
}
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public override int GetHashCode()
{
const int prime1 = 899811277;
const int prime2 = 472883293;
int hash = prime1;
unchecked
{
hash = hash * prime2 + this.Name.GetHashCode();
if (this.Father != null)
{
hash = hash * prime2 + this.Father.GetHashCode();
}
}
return hash;
}
public override string ToString()
{
return this.Name;
}
public static bool operator==(Person x, Person y)
{
if (Object.ReferenceEquals(x, null))
return Object.ReferenceEquals(y, null);
else
return x.Equals(y);
}
public static bool operator!=(Person x, Person y)
{
return !(x==y);
}
}