1

次のように公開するこのクラスがあり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;
        }
    }

ARecordには、他の多くのプロパティ(したがって、「メモリ/ストレージに関して」非常に大きい)の中で、Idプロパティ(Key2つの「部分」で構成されるオブジェクト)があります。完全を期すために、これは次のようになります。

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()で十分です。はすでに実装されており、実装がまったく難しくない が必要な場合があります。しかし、私は(私が思うに)脱線します..KeyIEquatableIEQualityComparer<Key>

Intersect()ドキュメントには次のように記載されています。

このメソッドによって返されたオブジェクトが列挙されると、 Intersect は を列挙し、firstそのシーケンスのすべての個別の要素を収集します。次に、 を列挙secondし、両方のシーケンスで発生する要素をマークします。最後に、マークされた要素が収集された順序で生成されます。

(私のものを強調)

したがって、私が正しく理解していればfirstIEnumerable<Record>ファイルはまだ完全にメモリに読み込まれます。そして、それがsecondすべて私の「処理対象」の「リスト」と一致したとしても、依然として非常に大量のデータである可能性があるメモリに読み込まれます。または、ドキュメントを読み違えていますか?これは「最終的に」つまずいたり、ドキュメントを誤解していますか?

私が防ぎたいのは、明らかに、

  • a) データの一部を 1 つずつ処理するという唯一の目的で大量のデータをメモリに読み込まないこと)
  • b)「処理対象」の「リスト」の各レコードに対して同じファイルを何度も(再)開かない(したがって、イテレータをリセットしないように注意したい)

簡単に言えば。私がIntersect()やりたいことをしますか?別の方法を使用する必要がありますか? ネストされた for ループ? これを効率的に処理する方法に関する他のアイデアはありますか?

編集:「処理されるキーのリスト」が実際にはHashSet<Key>.


Ps私はベッドでこの目的のためにLinqを使用することについて脳波に襲われたばかりで、これを理解する前に眠ることができません. 残念ながら、私は休暇中で、まともな Visual Studio インスタンスから何マイルも離れているので、これをテストするだけです。それは私の退役後まで待たなければならないでしょう(だからミスは言っています...それについては見ていきます...)スマイリー

4

1 に答える 1

2

編集:私はあなたが実際に欲しいと思う:

var records = new SomeFileReader().Records()
                                  .Where(record => keys.Contains(record.Key));

foreach (var record in records)
{
    Process(record);
}

ドキュメンテーションは間違っています、Intersect恐れ入ります。それは実際にはsecond最初に列挙し、その中のすべてを収集します...そして stream first、交差する値を生成します。

また、要素を生成する前に、すべての要素が表示されるまで待機しません。実際の動作の詳細については、 Edulinq のブログ投稿をIntersect参照してください。

TL;DR の意味では、次のとおりです。

  • HashSet<T>から作成するsecond
  • 繰り返すfirst
    • アイテムごとに、セットから削除してみてください
    • それセット内にある場合は、それを譲ります。そうでなければ、しないでください

firstアイテムがセットから削除されるという事実は、同じ要素が 2 回生成されるのを防ぎます (セットであるため、との両方で 2 回以上発生したとしてもsecond)。

基本的に、オペランドの順序を逆にすれば大丈夫だと思うので、次のようにします。

var result = streamingRecordsFromFile.Intersect(smallCollectionInMemory);
于 2012-07-25T00:10:48.747 に答える