我有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
的不同列表。
问题在于这种机制确实很慢
答案 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);