在一个大的List上测试大量字符串的最有效方法
本文关键字:测试 字符串 方法 有效 一个 List string | 更新日期: 2023-09-27 18:01:41
我看过许多其他类似的问题,但是给出的方法对于我想要完成的任务来说似乎太慢了,或者正在测试部分匹配,这是我不需要的,应该更慢。
我有两个充满字符串的大文件,我需要检查一个列表中的每个字符串,看看它是否与第二个列表中的任何字符串匹配。我不需要检查部分匹配,所有内容都应该被正确转义。
第二个列表(要删除的字符串)包含160,000个字符串。我已经将其加载到List<String>
中,然后读取大文件的每一行,并使用List<String>.Any(line.contains)
进行测试。
即使只是第一个列表的一小部分(40k个字符串),这也需要很长时间,在我快速开发的计算机上可能需要20多分钟。
这是我的问题
在不需要部分匹配的情况下,将一个大字符串列表单独与另一个更大的字符串列表进行比较,有没有更有效的方法?
不使用List<string>
,使用HashSet<string>
。它有O(1)次查找而不是像列表那样有O(n)次查找。如果你做了这个改变,你应该会看到数量级的加速。
使用HashSet.Contains()
而不是LINQ的.Any()
扩展方法可能会给你带来更好的性能。
把这个放在一边,你的性能问题是由于列表数据结构的性质;它的设计不是为了通过"Any"有效地搜索。
问题是"Any"只是简单地进行线性搜索,从列表的开头开始搜索,直到找到匹配项。如果列表有100K个条目,那么每次"miss"将进行100K个字符串比较,每次"hit"将平均进行50K个字符串比较。
可怕的。
你应该做的是将List转换为字符串的HashSet。该集合占用的内存稍微多一些,但是非常快。
另一种可能的优化是对其中一个列表进行排序——这是一个O(nlgn)的操作——然后对排序后的列表进行二叉搜索,这是一个O(lgn)的操作。
第三种可能的优化是对两个列表进行排序,然后编写一个排序列表比较器。显然,排序列表比较器要比未排序列表比较器快得多。您保留每个列表的索引,并且只推进指向"较小"项的索引。也就是说,如果列表是
A, B, C, D, G, I
B, D, E, H, I
然后从指向A和B的索引开始。A比较小,所以你把第一个索引推进到B。现在它们是一样的;你找到匹配了。把它们都推进。第一个指标是"C",第二个指标是"D"。
更一般地说,我认为你对问题的描述太低了。我觉得你应该问洞的时候问的是钻头的问题。也许钻一开始就不是合适的工具。你能告诉我们为什么要匹配两个大的字符串列表吗?也许有一个更简单的方法来做你想做的事。
使用Union
和Except
运算符来获得两个列表之间的异同
联盟: http://msdn.microsoft.com/en-us/library/bb341731.aspx
除了:http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except.aspx
每个函数返回一个包含结果数据的列表。
我不明白为什么没有人提到Enumerable.Intersect
。这是一个非常有效和简单的函数。
您可以将第一个列表中的每个元素插入HashSet<string>
,然后测试第二个列表中的每个元素是否存在于该集合中。这只会命中每个条目一次,并且插入和测试应该是O(1)(除非您的数据集由于某种原因是病态的)
字符串是预先知道的,因此排序向量将比散列映射更快。使用字符串友好的散列(如FNV)对较小文件中的字符串进行散列,并将它们放入
中。vector<pair<int, string> >
定义函数使其在哈希上可排序和可比较,然后sort
vector
bool operator < (pair<int, string> const&, pair<int, string> const&) { ... }
bool operator < (pair<int, string> const&, int) { ... }
bool operator < (int, pair<int, string> const&) { ... }
现在从大文件中读取每个字符串,对其进行散列并使用equal_range
搜索向量。只比较哈希值匹配的完整字符串。(注意:可能需要额外的魔法来搜索哈希,而不是使用假pair<int, string>
。)
如果在输出开始之前可以接受较长的延迟,并且有足够的空间可用,那么将两个文件加载到排序向量中,使用set_intersection
查找匹配的哈希并比较它们的完整字符串可能会更快。我将把细节留给读者作为练习(:)。请记住,两边可能存在哈希冲突