我有一个公开IEnumerable<Record>
如下的类(省略了实现细节):
public class SomeFileReader() {
public IEnumerable<Record> Records()
{
using (StreamReader sr = new StreamReader(this.Path, this.Encoding, true))
{
var hdr = this.HeaderParser.Parse(sr.ReadLine()); //Parse, but further ignore header (the HeaderParser might throw though)
while (!sr.EndOfStream)
yield return this.RecordParser.Parse(sr.ReadLine()) as Record;
}
}
Record
除了许多其他属性(因此是相当大的“内存/存储方式”)之外,A还具有一个Id
属性(这是一个Key
由 2 个“部分”组成的对象)。为了完整起见,这看起来像:
public class Key : IEquatable<Key>
{
public string OperatorCode { get; set; }
public string Key { get; set; }
public bool Equals(Key other)
{
return (this.OperatorCode.Equals(other.OperatorCode, StringComparison.OrdinalIgnoreCase))
&& (this.Key.Equals(other.Key, StringComparison.OrdinalIgnoreCase));
}
}
该文件包含“键顺序”中的记录,因此(保证)按记录的ID
磁盘排序。
在内存中,我还有一些HashSet<Key>
我想从SomeFileReader
. 目前我的测试文件只有几兆字节,但我预计在不久的将来它会变得非常大。此时,我只是将整个文件读入内存Dictionary<Key, Record>
,以便从“待处理”记录的“列表”中轻松/快速地检索我想要处理的特定记录。这将类似于:
var recordsfromfile = MyFileImporter.Records().ToDictionary(k => k.Key.Key);
当然,一旦文件增长(太大),这将是一个问题。
但是由于我正在公开IEnumerable<Record>
我在想...我不应该将文件完全读入内存,因为记录是按关键顺序排列的。一个简单的Intersect()与我的待处理密钥“列表”就足够了。Key
已经实现了IEquatable
,如果我需要一个根本IEQualityComparer<Key>
不难实现的。但我(想我)离题了。。
Intersect()
文档告诉我:
当枚举此方法返回的对象时,Intersect 枚举
first
,收集该序列的所有不同元素。然后它枚举second
,标记出现在两个序列中的那些元素。最后,标记的元素按照它们被收集的顺序产生。
(强调我的)
所以,如果我理解正确,如果first
是我IEnumerable<Record>
的文件仍将完全读入内存。即使它second
与我的“待处理”“列表”完全匹配,仍然会被读入内存,这仍然可能是非常大量的数据。还是我误读了文档,这是“终于”让我绊倒和/或我误解了文档?
显然,我想要阻止的是
- a)不将大量数据读取到内存中,其唯一目的是一个接一个地处理其中的一些记录,之后我不关心这些记录(例如,处理会将结果写到其他地方)
- b)不要(重新)为我的“待处理”“列表”中的每条记录一次又一次地打开同一个文件(所以我要小心不要重置我的迭代器)
长话短说; 会Intersect()
做我想做的事吗?我应该使用其他方法吗?嵌套for循环?关于如何有效处理这个问题的任何其他想法?
编辑:更新以明确“要处理的密钥列表”实际上是一个HashSet<Key>
.
Ps 我刚刚被一个关于在床上使用 Linq 用于此目的的脑电波击中,在我弄清楚之前无法入睡。不幸的是,我正在度假,距离一个体面的 Visual Studio 实例数英里远,只是为了简单地测试一下。那将不得不等到我休假之后(所以小姐们说......我们会看到......)