在制作智能感知/自动完成列表时,过滤字符串列表的最快方法是什么?

时间:2010-12-31 23:57:30

标签: c# .net wpf linq intellisense

我正在编写一个Intellisense / Autocomplete,就像你在Visual Studio中找到的那样。在列表中包含2000多个项目之前,这一切都很好。

我正在使用简单的LINQ语句进行过滤:

var filterCollection = from s in listCollection
                       where s.FilterValue.IndexOf(currentWord,     
                       StringComparison.OrdinalIgnoreCase) >= 0
                       orderby s.FilterValue
                       select s;

然后我将此集合分配给WPF列表框的ItemSource,这就是它的结束,工作正常。

注意到,Listbox也是虚拟化的,因此在内存和可视树中最多只有7-8个可视元素。

然而,现在需要注意的是,当用户在richtextbox中输入速度非常快时,在每个键上执行过滤+绑定时,会出现这种半竞争状态或不同步过滤,如第一个键笔划的过滤仍然可以进行过滤或绑定工作,而第四个关键笔划也是如此。

我知道在应用过滤器之前我可能会延迟,但我正在尝试实现无缝过滤,就像在Visual Studio中那样。

我不确定我的问题到底在哪里,所以我也将它归因于IndexOf的字符串操作,或者我的字符串列表可以在某种索引中进行优化,这可以加快搜索速度。

对代码示例的任何建议都非常欢迎。

感谢。

5 个答案:

答案 0 :(得分:1)

我建议尝试将结果集限制在某些项目上并查看问题是否消失。也就是说,您可能有5000种可供选择,但尝试返回不超过100,即使更多匹配。说:

var filterCollection = (from s in listCollection
  where s.FilterValue.IndexOf(currentWord,StringComparison.OrdinalIgnoreCase)>=0
  orderby s.FilterValue
  select s).Take(100);

如果问题消失,可能是因为列表框返回的项目太多而导致速度减慢。我不确定问题会消失,因为ListBox是虚拟化的,但值得一试。您也可以尝试相同的操作,但在排序之前将过滤结果限制为100个项目(即orderby),看看是否有帮助。无论如何,按此顺序执行它会更有效:

var filterCollection = (from s in listCollection
  where s.FilterValue.IndexOf(currentWord,StringComparison.OrdinalIgnoreCase)>=0
  select s).Take(100)
           .OrderBy(s => s.FilterValue);

底线是确定问题是否是返回并分配给filterColection的项目数或初始项目数,或两者兼而有之。

答案 1 :(得分:1)

如果您有2000个项目的结果集,则延迟不是您的问题。我在这里做了一些大的假设,但你只需要最多返回500个项目 - 你的用户将继续输入以缩小结果集,直到它可以接受浏览大小。

你应该优化常见的情况(我假设它最终会说~50项) - 如果你的用户正在滚动2000个项目的小列表,那么其他错误,界面需要更多的工作。

答案 2 :(得分:1)

我认为问题在于您的过滤器执行O(n)(其中n是要自动完成的项目总数),也就是说,它必须通过每个项目找出哪些匹配。您拥有的物品越多,过滤器的性能就越差。

请尝试使用trie,而不是使用列表。尝试执行O(m),其中m是字符串中的字符数。这意味着数据集的大小不会影响查找的性能。

Promptu(我编写的应用启动器)中,我在intellisense / autocomplete中使用了try。如果您想查看尝试的示例,可以下载并试用。

答案 3 :(得分:0)

一个非常简单的优化是

if(currentWord.StartsWith(lastWord))

您可以过滤上一个查询返回的过滤项目列表。也就是说,除非您按照其他一些答案的建议减少LINQ查询返回的项目数。您可以随时将查询中的内容存储在变量中,然后执行Take(100),尽管在这种情况下您需要确保LINQ的延迟执行不会让您感到厌烦。

在绑定方面,您可以使用ObservableCollection而不是替换整个集合,只需添加/删除项目即可。如果您要这样做,反转过滤器返回的内容将是一个好主意,但如果用户快速输入,您会看到更快的响应并且不会看到如此大的性能损失。

答案 4 :(得分:0)

这是你的“竞争条件”。

orderby s.FilterValue

考虑字母序列d,o,g。

  • “d”开始运行,并且会匹配设置的30%。必须订购6000件物品。
  • “do”开始运行并且匹配6%的集合。必须订购1200件商品。
  • “dog”开始跑步,并且会匹配该组的0.5%。必须订购100件商品。

在考虑每个事件的不同工作负载时,最后一个事件将在第一个和第二个事件之前完成也就不足为奇了。

我能想象的最正确的行为是在事件开始时阻止对任何先前活动事件的绑定。如果你可以通过停止对这些事件的执行来阻止绑定,那就更好了。回想那些传单,他们不再有目标了。