2013-01-08 60 views
1

我有一個ListObjects(大約100k),必須重複以產生一個Dictionary。然而 代碼是在一行Linq的性能低下凡其他語言

public class Item{ 
     public int ID; 
     public int Secondary_ID; 
     public string Text; 
     public int Number; 
} 

數據看起來是這樣(100K線)

ID | Secondary_ID |  Text  | Number 
1 | 1   | "something"  | 3 
1 | 1   | "something else"| 7 
1 | 1   | "something1" | 4 
1 | 2   | "something2" | 344 
2 | 3   | "something3" | 74 
2 | 3   | "something4" | 1 

進行得非常緩慢,特別是,我想它看起來像這樣結束的時候。 (任何集合會做,說實話)

Dictionary<int, string> 

Key    | Value 
(secondary_ID) | (Text : Number) 

1    | "Something : 3, Something else : 7, Something1 : 4" 
2    | "Something2 : 344" 
3    | "Something3 : 74, Something4 : 1" 

我的代碼目前是這樣的ListAll包含的所有數據。

var Final=new Dictionary<int, string>(); 
var id1s=ListAll.Select(x => x.ID).Distinct().ToList(); 

foreach(var id1 in id1s) { 
    var shortList=ListAll.Where(x => x.ID==id1).ToList(); //99% of time spent is here 
    var id2s=shortList.Select(x => x.Secondary_ID).Distinct().ToList(); 

    foreach(var id2 in id2s) { 
     var s=new StringBuilder(); 
     var items=shortList.Where(x => x.Secondary_ID==id2).ToList(); 

     foreach(var i in items) { 
      s.Append(String.Format("{0} : {1}", i.Text, i.Number)); 
     } 

     Final.Add(id2, s.ToString()); 
    } 
} 

return Final; 

現在輸出然而正確的,因爲在上面的評論說,這需要一個非常長的時間來處理(90秒 - 肯定比我與舒適),並想知道是否有更快的方式實現這一點。

此代碼只會被使用一次,所以不是一個真正的正常用法,通常我會因爲這個原因而忽略它,但想知道學習的目的。

+0

那不是你真正的代碼,是嗎?文本,數字變量不存在,並且您也沒有向stringbuilder添加任何逗號... – digEmAll

+0

輸入數據在哪裏?如果你編寫一個運行於其上的IQueryable,你可能會獲得更好的性能。 SQL數據庫 – Rob

+0

你有很多冗餘的ToList調用。沒有必要將一個'IEnumerable'轉換成列表,當你要做的唯一事情就是在'foreach'中迭代它或調用另一個LINQ方法。這只是浪費處理器時間和內存。 – Servy

回答

7

通過ID對項目進行分組的方法更有效率(甚至更容易)是使用GroupBy

var query = ListAll.GroupBy(x => x.Secondary_ID) 
    .ToDictionary(group => group.Key, 
     group => string.Join(", ", 
      group.Select(item => string.Format("{0} : {1}",item.Text , item.Number))), 
    //consider refactoring part of this line out to another method 
    }); 

至於你的代碼太慢的原因,你正在搜索整個列表中每個不同的ID。這是一個O(n^2)操作。 GroupBy不這樣做。它在內部使用一個基於散列的結構,根據你分組的內容,以便它可以快速(在O(1)時間)找到任何給定項屬於的桶,而不是O(n)採取你的方法。

+0

該op的代碼看起來比O(n^2)更差 – Magnus

+0

@Magnus它並非如此。在嵌套層次上的代碼具有更高的漸近複雜性(n^3),但這並不是他說他大部分時間都是基於分析進行花費的地方。還要注意內層不是基於整個列表中的項目數量; n是基於不同ID的數量,所以雖然這造成了非常高的最壞情況,但是對於他的實際數據來說,很可能內層真的不是問題。也就是說,如果他沒有準確地分析代碼,那麼下一個級別可能確實是問題所在。 – Servy

+0

這真是太棒了謝謝你,我最初想使用to字典方法,但不知道如何實現字符串部分字符串.join解決了這個問題 – RoughPlace

8

這裏是我會做什麼(未經測試,但希望你的想法):

var final = ListAll.GroupBy(x => x.Secondary_ID) 
        .ToDictionary(x => x.Key, x => String.Join(", ", 
         x.Select(y => String.Format("{0} : {1}", 
          y.Text, y.Number))) 

這第一組由Secondary_ID使用GroupBy,然後把結果到使用ToDictionary字典。

GroupBy意願將數據分組分成以下幾組:

 
Key = 1: 

ID | Secondary_ID |  Text  | Number 
1 | 1   | "something"  | 3 
1 | 1   | "something else"| 7 
1 | 1   | "something1" | 4 

Key = 2: 
ID | Secondary_ID |  Text  | Number 
1 | 2   | "something2" | 344 

Key = 3: 
ID | Secondary_ID |  Text  | Number 
2 | 3   | "something3" | 74 
2 | 3   | "something4" | 1 

然後.ToDictionary方法:

  • 選擇鍵爲x.Key(我們分組上的鍵,即Secondary_ID)。
  • 選擇String.Join操作的結果作爲值。正在加入的是從該組內的元素收集「Text:Number」 - x.Select(y => String.Format("{0} : {1}", y.Text, y.Number)
0

首先,到處去除ToList(),它應該變快;因爲ToList()執行渴望評價

我覺得你的代碼需要做的是:

var Final=new Dictionary<int, string>(); 

foreach(var x in ListAll) 
    if(Final.ContainsKey(x.Secondary_ID)) 
     Final[x.Secondary_ID]+=String.Format(", {0} : {1}", x.Text, x.Number); 
    else 
     Final.Add(x.Secondary_ID, String.Format("{0} : {1}", x.Text, x.Number)); 

return Final; 

一個Dictionary不能包含重複的鍵,所以它無論在這裏使用IDSecondary_ID,如果你的Secondary_ID必須在現有的ID;你甚至不需要代碼中的Distinct()

做一些簡化,原來的代碼是:

foreach(var id1 in ListAll.Select(x => x.ID).Distinct()) { 
    foreach(var id2 in ListAll.Where(x => x.ID==id1).Select(x => x.Secondary_ID).Distinct()) { 
     var s=new StringBuilder(); 

     foreach(var i in ListAll.Where(x => x.ID==id1).Where(x => x.Secondary_ID==id2)) { 
      s.Append(String.Format("{0} : {1}", i.Text, i.Number)); 
     } 

     Final.Add(id2, s.ToString()); 
    } 
} 
+0

爲什麼要在'Dictionary'上使用'SortedDictionary'?它的表現不會很好,結果也不需要排序,這是您想要使用它的唯一原因。 – Servy

+0

@Servy:更新,謝謝。 –