如何提高搜索效果

时间:2019-02-09 20:29:59

标签: c# linq

我有Country个具有这种设计的对象的列表:

public class Country
{
   public string Name { get; set; }
   public string ISO { get; set; }
}

我需要检查特定列表中是否存在Country,所以我做了:

foreach (var date in dates)
{
    var newCountries = GetCountryByDate(date);

    foreach (var country in newCountries)
    {
        if (!countries.Exists(c => c.ISO == country.iso))
            countries.Add(new Country
            {
                Id = country.id,
                Name = country.name,
                ISO = country.iso,
                Link = country.id.ToString()
            });
    }
}

其中newCountries包含需要迭代的Country的列表,而countries是按日期过滤的List<Country>,实际上每个date都包含一个Countries的不同列表。

问题在于这种机制确实很慢

1 个答案:

答案 0 :(得分:3)

您正在通过countries属性检查ISO列表中的新国家。

与其为每个新国家/地区循环列表(为O(n 2 )),请为HashSet<string>属性生成ISO,这会将搜索算法转换为O( n)。

var existedISOCodes = countries.Select(country => country.ISO).ToHashSet();

foreach (var date in dates)
{
    var newCountries = GetCountryByDate(date);

    foreach (var country in newCountries)
    {
        if (existedISOCodes.Add(country.iso))
        {
            countries.Add(new Country
            {
                Id = country.id,
                Name = country.name,
                ISO = country.iso,
                Link = country.id.ToString()
            });
        }
    }
}

HashSet<T>.Add方法将尝试将给定值添加到集合中,如果值已存在,则返回false

如果您是LINQ的粉丝:

var existedISOCodes = countries.Select(country => country.ISO).ToHashSet();
var newCountries = 
    dates.SelectMany(date => GetCountryByDate(date))
         .Where(country => existedISOCodes.Add(country.ISO))
         .Select(country => 
         {
             return new Country
             {
                 Id = country.id,
                 Name = country.name,
                 ISO = country.iso,
                 Link = country.id.ToString()
             };
         });

countries.AddRange(newCountries);

但是我认为实际的性能瓶颈是GetCountryByDate方法。
如果此方法访问一些外部资源(数据库,Web服务),而您无法在一个请求中获取所有日期的国家/地区,则可以将GetCountryByDate转换为异步功能,那么您将可以获取所有日期的国家/地区几乎同时

var newCountryTasks = dates.Select(date => GetCountryByDateAsync(date));
await Task.WhenAll(newCountryTasks);

var newCountries = 
    newCountryTasks.SelectMany(task => task.Result)
                   .Where(country => existedISOCodes.Add(country.ISO))
                   .Select(country => 
                   {
                       return new Country
                       {
                           Id = country.id,
                           Name = country.name,
                           ISO = country.iso,
                           Link = country.id.ToString()
                       };
                   });

countries.AddRange(newCountries);