使用列表和Linq,编写高效的代码
本文关键字:高效 代码 列表 Linq | 更新日期: 2023-09-27 18:16:36
我创建了一个快速控制台应用程序,它创建了10000个年轻人和10000个老年人,并将它们添加到两个单独的列表中。然后执行一些查询以获得基于个性的信息。
class Program
{
static void Main(string[] args)
{
private Random random = new Random();
private List<Person> youngerPersons = new List<Person>();
private List<Person> olderPersons = new List<Person>();
private long samePersonalityMatches = 0;
for (int i = 0; i < 10000; i++)
{
youngerPersons.Add(new Person(RandomString(10), DateTime.Now.ToString(), RandomString(4), random.Next(10, 50),(Person.PersonalityType)random.Next(0, 4), i));
}
for (int i = 0; i < 10000; i++)
{
olderPersons.Add(new Person(RandomString(10), DateTime.Now.ToString(), RandomString(4), random.Next(51, 120),(Person.PersonalityType)random.Next(0, 4), i));
}
//Example query 1
foreach (Person person in youngerPersons.Where(w => w.Id > 4567 && w.Age > 70))
{
Console.WriteLine(person.Id);
}
//Example query 2
foreach (Person person in youngerPersons)
{
foreach (Person olderPerson in olderPersons)
{
if (person.Personality == olderPerson.Personality)
{
samePersonalityMatches++;
}
}
}
Console.WriteLine("Number of matches: " + samePersonalityMatches);
}
private static Random random = new Random((int)DateTime.Now.Ticks);
private static string RandomString(int size)
{
StringBuilder builder = new StringBuilder();
char ch;
for (int i = 0; i < size; i++)
{
ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
builder.Append(ch);
}
return builder.ToString();
}
}
internal class Person
{
public enum PersonalityType
{
Funny = 0,
Boring = 1,
Serious = 2,
Grumpy = 3,
Normal = 4
}
public Person(string name, string dateofbirth, string nickname, int age, PersonalityType personalityType, int id)
{
this.Name = name;
this.Dateofbirth = dateofbirth;
this.Nickname = nickname;
this.Age = age;
this.Id = id;
this.Personality = personalityType;
}
public string Name { get; set; }
public string Dateofbirth { get; set; }
public string Nickname { get; set; }
public int Age { get; set; }
public int Id { get; set; }
public PersonalityType Personality { get; set; }
}
基本上,我想了解最佳实践,以获得我放入代码中的两个示例查询的最大性能。我已经阅读了一些关于使用intersect的性能相关材料,但我不确定使用哪种以及何时使用最好以获得最佳性能。列表有点太大了(大小方面),但它使示例2运行起来更有趣。
一个很好,非常接近最优,我会让它像你有它(记住,程序员的时间比机器的时间更昂贵)。
对于两个,你可以做得更好。你遍历olderPersons
列表的次数太多了,让我们看看能不能把它减少到一次遍历。
Dictionary<Personality, int> dict =
youngerPersons.GroupBy(p => p.Personality)
.ToDictionary(g => g.Key, g => g.Count());
long samePersonalityMatches =
olderPersons.Select(
q => dict.ContainsKey(q.Personality) ?
dict[q.Personality] : 0
)
.Sum();
然后,一旦我们看到这个,我们应该对自己说,嘿,这看起来像一个哈希连接!然后,我们可以写得更清楚:
long samePersonalityMatches =
youngerPersons.Join(
olderPersons,
p => p.Personality,
q => q.Personality,
(p, q) => null
)
.Count();
任何时候你看到模式嵌套循环,匹配超过外部,内部你应该考虑一个连接!
第一个示例查询很好,您可能无法做任何事情来提高其性能。
在第二个查询中,可以使用join:
samePersonalityMatches =
youngerPersons.Join(olderPersons,
p => p.Personality,
p => p.Personality,
(left, right) => null) // the result doesn't really matter,
// since we just want the count
.Count();
或者如果您喜欢查询语法:
samePersonalityMatches =
(from y in youngerPersons
join o in olderPersons
on y.Personality equals o.Personality
select null).Count();
一个join只枚举每个列表一次,所以复杂度是O(M + N)
,而不是O(M * N)
Jason和Thomas的具体答案假设(考虑到你的问题的措辞方式,这是相当合理的)你在获得数据之前不知道这些问题。
我只是想指出,如果你知道你要问你的数据的问题,你可以继续运行聚合,当你把年轻人和老年人添加到你的列表,这样答案总是准备好了。
这与选择在数据库中构建特定索引的动机类似。