我创建了一个快速控制台应用程序,它创建了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; }
}
基本上我想了解最佳实践,以便从我在代码中添加的两个示例查询中获得最佳性能。我已经阅读了一些关于使用相交的性能相关材料,但我不确定哪种以及什么时候最好用来获得最佳性能。列表有点OTT(大小明智),但它使示例两个更有趣的运行。
答案 0 :(得分:3)
一个很好,非常接近最优,我会像你一样离开它(记住,程序员时间比机器时间更贵)。
对于两个你可以做得更好。你走了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();
然后,一旦我们看到这一点,我们应该对自己说,嘿,这看起来像hash join!然后,我们可以更清楚地写出来:
long samePersonalityMatches =
youngerPersons.Join(
olderPersons,
p => p.Personality,
q => q.Personality,
(p, q) => null
)
.Count();
任何时候你看到模式嵌套循环,匹配外部,内部你应该考虑加入!
答案 1 :(得分:3)
第一个示例查询很好,您可能无法改进其性能。
在第二个查询中,您可以使用联接:
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();
联接只枚举每个列表一次,因此复杂度为O(M + N)
,而不是O(M * N)
答案 2 :(得分:0)
杰森和托马斯的具体答案假设(非常合理地给出了你的问题的表达方式),在获得数据之前你不知道问题。
我想我已经指出,如果您确实知道要询问数据的问题,那么当您将年轻人和老年人添加到列表中时,您可以继续运行聚合,以便随时准备好答案。
这与选择在数据库中构建特定索引的动机类似。