0

我创建了一个快速控制台应用程序,它创建了 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 的性能相关材料,但我不确定哪个以及何时最适合使用以获得最佳性能。这些列表有点 OTT(大小明智),但它使示例 2 运行起来更有趣。

4

3 回答 3

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();

然后,一旦我们看到这个,我们应该对自己说,嘿,这看起来像一个哈希连接!那么,我们可以写得更清楚:

long samePersonalityMatches = 
    youngerPersons.Join(
                      olderPersons,
                      p => p.Personality,
                      q => q.Personality,
                      (p, q) => null
                  )
                  .Count();

任何时候你看到模式嵌套循环,匹配外部,内部你应该考虑一个连接!

于 2013-06-04T00:19:23.117 回答
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)

于 2013-06-04T00:20:05.047 回答
0

Jason 和 Thomas 在这里给出的具体答案假设(考虑到您的问题的措辞方式相当合理)您在获得数据之前并不知道这些问题。

只是想我会指出,如果您确实知道要询问数据的问题,则可以在将年轻人和老年人添加到列表中时继续运行聚合,以便随时准备好答案。

这与选择在数据库中构建特定索引的动机类似。

于 2013-06-04T00:27:07.173 回答