2013-02-07 39 views
0

我有一個簡單的類:LINQ(to Object)查詢是否可能包含無限循環?

public class RawBomItem 
{ 
    private string material; 
    private string item; 
    private string component; 
    private string quantity; 
    private string b; 
    private string spt; 
... 
} 

和每一個數據成員有一個屬性。

然後,我有一個包含這個類

private List<RawBomItem> rawBom; 

的情況下,該列表包含超過70000項的列表。

在這一點上,我想在這個列表上運行一個複雜的LINQ查詢。

List<string> endProducts = new List<string>(
    rawBom.Where(x1 => new List<string>(rawBom.Select(x2 => x2.Component) 
               .Distinct()) 
          .Contains(x1.Material) && (x1.B != "F")) 
      .Select(x3 => x3.Material)); 

查詢看起來好像進入了一個無限循環。 (我已經等了幾分鐘才關閉它)

我會把它變成數據庫工作,我只是對什麼可以是問題感興趣。

+0

爲什麼你需要在一行中做到這一點? – LukeHennerley

+5

好吧,試着將列表長度減少到幾百,看看它是否仍然有效... –

+5

這個查詢的作用還不太清楚,但考慮它會過濾70K項目。對於每個處理70K項目,每個項目最多兩次。所以我們正在研究大約5到10億次**迭代。這應該需要一些時間。換句話說:O(N²)不好。 – Jon

回答

9

我不明白應該如何進行無限循環,但是代碼效率極低。
對於rawBom中的每個項目,您都可以計算不同的組件並將它們複製到新列表中。因此,如果列表中有70,000個項目,則執行70.000^2 = 4900,000,000次迭代。此外,對於列表中的每個項目,您都會再次對不同組件的列表進行迭代。根據您擁有多少個不同的組件,您可以在頂部添加相同數量的迭代。

這可以改進:

var components = new HashSet<string>(rawBom.Select(x => x.Component).Distinct()); 
var endProducts = rawBom.Where(x => components.Contains(x.Material) && 
            x.B != "F") 
         .Select(x => x.Material) 
         .ToList(); 
  1. 我們提取主查詢不同分量的列表的創建,所以我們要計算它只有一次 - 而不是70.000倍。
  2. 我們使用HashSet<string>而不是List<string>。這將呼叫從O(n)更改爲ContainsO(1)

最終結果是,您只列出兩次列表,導致只有140,000次迭代。現在將其與原始迭代次數進行比較。

+1

我喜歡使用'HashSet '而不是'List '。絕對是一個認識到這一點的+1。 –

+0

不是'new Hashset()'執行自己的'Distinct()',而不需要自己做? – ErikE

0

我會簡化你的查詢一點點。我認爲這是你想要的?

List<string> endProducts = rawBom 
.Where(x1 => rawBom.Any(x2 => x2.Component == x1.Material) && x1.B != "2") 
.Select(x1 => x1.Material).ToList(); 

在rawBom每個項目這不會產生任何額外的名單,但會使用它本身

+0

它仍然迭代接近50億次。 –

+0

當然,使用HashSet要好得多。我沒有看到你的建議,當我寫我的:) –

+2

不應該是'.Any(x2 => x2.Component == x1.Material)'?原始文件正在創建組件列表,並查看列表是否包含「材質」,而不是包含「材質」是其子串的值。 – juharr

1

這是一個有點遙不可及,它是一個無限循環。除非你的序列是generator,但你沒有這個。

你有什麼是代碼效率低下,所有地獄。

rawBom.Where(// this goes over the 70,000 item collection once 

爲每個70,000您有:

new List<string>(rawBom.Select(x2 => x2.Component).Distinct()) // this goes over the 70,000 collection 3 times (first select, then distinct, then new list) 
.Contains // another bunch of accesses depending on how many distinct items there were 
.Select(x3 => x3.Material) // iterates over the resulting collection once 

所以這將是緩慢的,毫無疑問。

0
List<RawBomItem> list = new List<RawBomItem>(); 
var components = list.Select(x => x.component).Distinct(); 
var b = new Func<RawBomItem, bool>(x => 
    { 
    return components.Contains(x.Material) && x.B != "F"; 
    }); 
var v = list.Where(x => b(x)).Select(x1 => x1.material).ToList();