1

何千ものレコードをフォーマットで返す文字列型があります

key1,val1,val2,val3,val4,val5:key2,val6,val7,val8,val9,val10:key3,val11,val12,val13,val14,val15

これをKey、Listとして辞書に割り当てたいので、次のようになります。

key1、[val1、val2、val3、val4、val5]

key2、[val6、val7、val8、val9、val10]

key3、[val11、val12、val13、val14、val15]

。。。

すべてのキーは文字列内で一意であり、リストサイズはすべてのレコードで一定です。

現在、Splitを使用しており、各レコードをループしています。

    //short example string - may contain 1000's
    string newstr = @"key1,val1,val2,val3,val4,val5:key2,val6,val7,val8,val9,val10:key3,val11,val12,val13,val14,val15";

    Dictionary<string, List<string>> mydictionary = new Dictionary<string, List<string>>();
    foreach (string item in newstr.Split(':'))
    {
        List<string> list = new List<string>(item.Split(','));
        mydictionary.Add(list[0], list);        
    }

私の質問は、ループするのではなく、C#4.0を使用して数千のレコードに対してこれを行うためのより効率的で迅速な方法はありますか?

更新:さまざまな答えをテストした後、以下は「正しい」時間です

ここに画像の説明を入力してください

static void Main(string[] args)
{
    System.IO.StreamReader myFile =  new System.IO.StreamReader(@"C:\Users\ooo\Desktop\temp.txt");
    string newstr = myFile.ReadToEnd();
    myFile.Close();

    TimeSpan ts;
    TimeSpan te;
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();


    ts = stopWatch.Elapsed;
    Dictionary<string, List<string>> mydictionary = new Dictionary<string, List<string>>();
    foreach (string item in newstr.Split(':'))
    {
        List<string> list = new List<string>(item.Split(','));
        mydictionary.Add(list[0], list);
    }
    te = stopWatch.Elapsed;
    Console.WriteLine("MyTime: " + (te - ts).ToString());



    ts = stopWatch.Elapsed;
    var result = newstr.Split(':')
         .Select(line => line.Split(','))
         .ToDictionary(bits => bits[0],
                       bits => bits.Skip(1).ToList());
    te = stopWatch.Elapsed;
    Console.WriteLine("JonSkeet: " + (te - ts).ToString());


    ts = stopWatch.Elapsed;
    string[] keysAndValues = newstr.Split(':');
    var newdictionary = new Dictionary<string, List<string>>(keysAndValues.Length);
    foreach (string item in keysAndValues)
    {
        List<string> list = new List<string>(item.Split(','));
        newdictionary.Add(list[0], list);
    }
    te = stopWatch.Elapsed;
    Console.WriteLine("Joe: " + (te - ts).ToString());


    Console.WriteLine("Records: " + mydictionary.Count.ToString());


    stopWatch.Stop();
}
4

3 に答える 3

3

辞書は再割り当てを回避するために必要な容量で構築されているため、以下は潜在的に高速です。

//short example string - may contain 1000's     
string newstr = ...;

string[] keysAndValues = newstr.Split(':');
var mydictionary = new Dictionary<string, List<string>>(keysAndValues.Length);
foreach (string item in keysAndValues)     
{         
    List<string> list = new List<string>(item.Split(','));         
    mydictionary.Add(list[0], list);
    // remove key from list to match Jon Skeet's implementation
    list.RemoveAt(0);
} 

ただし、JonSkeetのLINQバージョンよりも読みにくいです。

于 2012-09-18T10:10:31.250 に答える
2

次のようなものが必要なようです。

var result = text.Split(':')
                 .Select(line => line.Split(','))
                 .ToDictionary(bits => bits[0],
                               bits => bits.Skip(1).ToList());

もちろん、これ以上効率的ではないかもしれません...あなたはそれが必要であると測定しましたか?これを「数千」のレコードに対してのみ実行している場合は、瞬く間に実行されると思います。さらに、このコードがボトルネックになる前に、IO(ネットワーク、ディスク)がボトルネックになると思います。

あなたのコメントから:

実際には非常に高速でボトルネックではありませんが、より迅速な代替手段がある場合は常にループを回避しようとします

そうしないでください。その仕事をする最も単純なコードを目指し、それが十分に機能するかどうかを確認します。個人的にはLINQベースのコードが好きですが、既存のコードでも問題ありません。より高速な代替手段は、書き込み、読み取り、および保守が大幅に困難になる可能性があります。利益がわずかである場合、なぜあなたはその努力に行くのですか?

私のコードにはリストの最初の値としてキーがないことに注意してください。これは仕様と一致しますが、サンプルコードとは一致しません。

于 2012-09-18T09:46:42.443 に答える
2

LINQソリューション(@JonSkeetのような)に対して投稿したコードを実行すると、LINQは現在のアプローチが1000を超えるレコードに対して行う時間の約2倍かかることがわかります。

したがって、あなたの質問に答えるには:

ループするのではなく、C#4.0を使用して数千のレコードに対してこれを行うより効率的で迅速な方法はありますか?

私はノーと言うでしょう。

ベンチマークテストコード:

 var value = "key{0},val1,val2,val3,val4,val5:";
 string newstr = "";
 for (int i = 0; i <= 1000; i++)
 {
     newstr += String.Format(value, i + 1);
 }

 var sw = new System.Diagnostics.Stopwatch();
 sw.Start();
 Dictionary<string, List<string>> mydictionary = new Dictionary<string, List<string>>();
 foreach (string item in newstr.Split(':'))
 {
     List<string> list = new List<string>(item.Split(','));
     mydictionary.Add(list[0], list);
 }
 sw.Stop();
 Console.WriteLine("Looping time: " + sw.Elapsed.ToString());
 sw.Reset();
 sw.Start();
 var result = newstr.Split(':')
                    .Select(line => line.Split(','))
                    .ToDictionary(bits => bits[0],
                                  bits => bits.Skip(1).ToList());
 sw.Stop();
 Console.WriteLine("LINQ time: " + sw.Elapsed.ToString());
 Console.ReadKey(); 
于 2012-09-18T10:04:55.650 に答える