当集合是有序的时,LINQ 可以使用二分搜索吗?
当我尝试搜索的集合已排序时,我可以以某种方式“指示”LINQ 使用二分搜索吗?我正在使用 ObservableCollection
,其中填充了有序数据,并且我正在尝试使用 Enumerable.First(<谓词>)。在我的谓词中,我按集合排序所依据的字段的值进行过滤。
Can I somehow "instruct" LINQ to use binary search when the collection that I'm trying to search is ordered. I'm using an ObservableCollection<T>
, populated with ordered data, and I'm trying to use Enumerable.First(<Predicate>). In my predicate, I'm filtering by the value of the field my collection's sorted by.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
据我所知,使用内置方法是不可能的。然而,编写一个允许您编写类似内容的扩展方法相对容易:(
当然,假设您的集合实现了 IList ;如果您无法随机访问项目,则无法执行二分搜索)
这是一个示例实现:
(未测试...可能需要进行一些调整)现在已测试并修复;)它抛出
InvalidOperationException
的事实可能看起来很奇怪,但这就是当没有匹配项时Enumerable.First
所做的事情。As far as I know, it's not possible with the built-in methods. However it would be relatively easy to write an extension method that would allow you to write something like that :
(assuming, of course, that you collection implements IList ; there's no way to perform a binary search if you can't access the items randomly)
Here's a sample implementation :
(not tested... a few adjustments might be necessary)Now tested and fixed ;)The fact that it throws an
InvalidOperationException
may seem strange, but that's whatEnumerable.First
does when there's no matching item.接受的答案非常好。
但是,我需要 BinarySearch 返回较大的第一个项目的索引,如
List.BinarySearch()
所做的那样。因此,我使用 ILSpy 观看了它的实现,然后我将其修改为具有选择器参数。我希望它对某人和对我一样有用:
The accepted answer is very good.
However, I need that the BinarySearch returns the index of the first item that is larger, as the
List<T>.BinarySearch()
does.So I watched its implementation by using ILSpy, then I modified it to have a selector parameter. I hope it will be as useful to someone as it is for me:
好吧,您可以在
ObservableCollection
上编写自己的扩展方法 - 但随后该方法将用于任何ObservableCollection
扩展方法可用,而不知道它是否已排序。您还必须在谓词中指出您想要查找的内容 - 使用表达式树会更好...但这会很难解析。基本上,
First
的签名并不适合二分搜索。我建议您不要尝试重载现有签名,而是编写一个新签名,例如
(我现在不打算实现它,但如果您愿意,我可以稍后实现。)
通过提供函数,您可以按集合排序的属性进行搜索,而不是按项目本身进行搜索。
Well, you can write your own extension method over
ObservableCollection<T>
- but then that will be used for anyObservableCollection<T>
where your extension method is available, without knowing whether it's sorted or not.You'd also have to indicate in the predicate what you wanted to find - which would be better done with an expression tree... but that would be a pain to parse. Basically, the signature of
First
isn't really suitable for a binary search.I suggest you don't try to overload the existing signatures, but write a new one, e.g.
(I'm not going to implement it right now, but I can do so later if you want.)
By providing a function, you can search by the property the collection is sorted by, rather than by the items themselves.
Enumerable.First(predicate)
适用于仅支持枚举的IEnumarable
,因此它无法随机访问其中的项目。此外,您的谓词包含最终结果为 true 或 false 的任意代码,因此无法指示测试项目是否太低或太高。为了进行二分搜索,需要此信息。
Enumerable.First(predicate)
只能在遍历枚举时按顺序测试每个项目。Enumerable.First(predicate)
works on anIEnumarable<T>
which only supports enumeration, therefore it does not have random access to the items within.Also, your predicate contains arbitrary code that eventually results in true or false, and so cannot indicate whether the tested item was too low or too high. This information would be needed in order to do a binary search.
Enumerable.First(predicate)
can only test each item in order as it walks through the enumeration.请记住,LINQ 使用的所有(?至少是大多数)扩展方法都是在
IQueryable
或IEnumerable
或IOrderedEnumerable
上实现的。 T>
或IOrderedQueryable
。这些接口都不支持随机访问,因此它们都不能用于二分搜索。 LINQ 之类的优点之一是您可以处理大型数据集,而无需从数据库返回整个数据集。显然,如果您还没有所有数据,则无法对某些内容进行二分搜索。
但正如其他人所说,您完全没有理由不能为
IList
或其他支持随机访问的集合类型编写此扩展方法。Keep in mind that all(? at least most) of the extension methods used by LINQ are implemented on
IQueryable<T>
orIEnumerable<T>
orIOrderedEnumerable<T>
orIOrderedQueryable<T>
.None of these interfaces supports random access, and therefore none of them can be used for a binary search. One of the benefits of something like LINQ is that you can work with large datasets without having to return the entire dataset from the database. Obviously you can't binary search something if you don't even have all of the data yet.
But as others have said, there is no reason at all you can't write this extension method for
IList<T>
or other collection types that support random access.