我有一个Items列表,每个都包含一个Type integer字段。
我想过滤我的列表,只获得与给定的整数列表匹配的项目。
我现在的代码有效,但我知道它可以优化。
Class Item
{
int ID;
//Other fields & methods that are irrelevant here
}
//Selection method
IEnumerable<Item> SelectItems(List<Item> allItems, List<int> toSelect)
{
return allItems.Where(x => toSelect.Contains(x.ID));
}
我遇到的问题是我遍历allItems
并在每次迭代中迭代toSelect
。
我觉得有可能更有效但我不知道如何用Linq实现这一目标。
这可能也是一个已被问到的问题,因为我不知道这是如何用英语调用的。这感觉有点愚蠢,因为我不知道如何在搜索引擎中正确地制定它。
答案 0 :(得分:5)
您可以使用效率更高的Join
,因为它使用基于集合的方法:
var selectedItems = from item in allItems
join id in toSelect
on item.Id equals id
select item;
return selectedItems;
另一种更有效的方法是使用HashSet<int>
而不是列表:
IEnumerable<Item> SelectItems(List<Item> allItems, HashSet<int> toSelect)
{
return allItems.Where(x => toSelect.Contains(x.ID));
}
答案 1 :(得分:3)
有两种方法可以解决这个问题。
目前,您有O(N×M)
表现(其中N
的大小为allItems
而M
的大小为toSelect
。
如果您只是想轻松地减少,那么您可以通过创建O(N)+O(M)
的哈希集将其缩减为toSelect
:
var matches = new HashSet<int>(toSelect);
return allItems.Where(x => matches.Contains(x.ID));
但是,这仍然会由N
占据主导地位 - allItems
的大小。
更好的长期方法可能是预先索引数据(以及保持索引)Id
。因此,allItems
不是List<T>
,而是Dictionary<int, T>
。请注意,构建字典可能很昂贵,因此您不希望每次要搜索时都这样做:关键是在开始时执行此操作(并保持维护) 。 然后这变为O(M)
(toSelect
的大小,通常很小),因为字典查找是O(1)
。
IEnumerable<Item> SelectItems(Dictionary<int, Item> allItems, List<int> toSelect)
{
foreach(var id in toSelect)
{
if (allItems.TryGetValue(id, out var found))
yield return found;
}
}
(不需要预先哈希toSelect
,因为我们没有检查Contains
)