【问题标题】:Split string to dictionary as key,list将字符串拆分为字典作为键,列表
【发布时间】:2012-09-18 09:42:45
【问题描述】:

我有一个字符串类型,它将以这种格式返回数千条记录

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]

。 . .

字符串中的所有键都是唯一的,所有记录的列表大小都是恒定的。

目前我正在使用拆分并使用循环每个记录

    //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 而不是循环处理 1000 条记录?

更新:测试了各种答案后,以下是“正确”时间

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();
}

【问题讨论】:

  • 您的值实际上是否以字符串形式出现?或者是否有任何值的“流式传输”?
  • 循环是必须要发生的,而且经常自己写是最快的。
  • 从外观上看,您当前的代码包含项目列表中的键 - 这是正确的/有问题吗?
  • 它们以字符串形式出现,格式显示为一个大字符串。是的,字符串还包括唯一键。
  • 您当前的实现是否存在性能瓶颈?除非您对数据进行预处理,否则无论使用 RegEx 或 LINQ 还是常规字符串操作,您总是必须像以前那样拆分字符串。

标签: c# c#-4.0


【解决方案1】:

以下可能会更快,因为字典的构造具有避免重新分配所需的容量:

//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);
} 

虽然比 Jon Skeet 的 LINQ 版本可读性差。

【讨论】:

  • 抱歉,Joe,我在那儿伤害了你。我已根据正在导入和处理的正确文件更新了原始帖子中的时间。
  • 通过更改列表初始化,您可能可以稍微 稍微更快。即用正确的容量初始化列表,然后用for(int i=1; commaSplits.Length; i++) ...添加数组的项目。 RemoveAt(0)List&lt;T&gt; 中效率很低...
  • @digEmAll,实际上采用 IEnumerable&lt;T&gt;List&lt;T&gt; 构造函数足够聪明,可以检查 ICollection&lt;T&gt;,如果是,则初始化为所需的容量。所以这不会有任何区别。 RemoveAt(0) 在这种情况下相当有效:它复制内部数组,但不重新分配。
  • @Joe:如果内部列表很长,内部副本可能需要一些时间,所以我指出,因为可以轻松避免开销。但是,我们在这里谈论的是秒的碎片......
  • @digEmAll,好的,我明白你现在的意思了,也明白你为什么说“稍微有点”了。
【解决方案2】:

听起来你想要这样的东西:

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

它可能不会再高效了,当然……你测量过它需要吗?如果您只是为“数千”条记录执行此操作,我希望它会在眨眼之间完成。此外,我希望任何 IO(网络、磁盘)在此代码出现之前都会成为瓶颈。

来自您的评论:

它实际上非常快而且不是瓶颈,但如果有更快的替代方案,我总是尽量避免循环

不要那样做。瞄准完成这项工作的最简单代码,然后检查它是否执行足够好。我个人更喜欢我的基于 LINQ 的代码,但您现有的代码也很好。任何更快的替代方案都可能最终变得更加难以编写、读取和维护。如果收益微不足道,您为什么要为此付出努力?

请注意,我的代码没有将键作为列表中的第一个值 - 它符合规范,但不符合您的示例代码。

【讨论】:

  • 更整洁,是的。但它是“更高效/更快”吗?
  • 您在Split(',') 之后错过了一个右括号
  • @Jon - 谢谢。我会测量速度并报告:)
【解决方案3】:

针对 LINQ 解决方案(如 @JonSkeet's)运行您发布的代码表明,对于 1000 多条记录,LINQ 所花费的时间大约是您当前方法的两倍。

因此回答你的问题:

有没有更有效/更快的方法来使用 C#4.0 而不是循环处理 1000 条记录?

我会说不。

基准测试代码:

 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(); 

【讨论】:

  • 这是不正确的。 LINQ 语句在被查询之前不会被评估 - 您所做的只是在此处设置查询,而不是枚举结果。此外,没有冒号分隔键/值列表对。
  • @DaveR。 +1 你的位置,我没有评估 LINQ 查询(或用: 分割数据!)。我已经更新了我的答案。
  • @DaveR.:这是不正确的。 ToDictionary() 执行立即评估,不需要ToList()
  • @digEmAll 事实上,你的权利。我第一次不确定,因为我只是在接受 Jon Skeet 的回答,但根据 documentation 它确实会立即进行评估。我已删除对ToList 的调用。
  • 对懒惰的评估混淆表示歉意。我误读了查询,@digEmAll 绝对正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-19
  • 2019-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多