2009-06-11 31 views
44

Phương pháp Linq Count() có nhanh hơn hoặc chậm hơn List<>.Count hoặc Array.Length không?Là số đếm Linq() nhanh hơn hoặc chậm hơn List.Count hoặc Array.Length?

+9

Cách dễ nhất để biết là dùng thử. Gói cả hai cuộc gọi đến các phương thức thích hợp trên StopWatch, thực hiện nó vài triệu lần và bạn sẽ biết. –

+1

Nó có thể không có giá trị gì mà sẽ không có một sự khác biệt đáng chú ý về tốc độ trừ khi chúng ta đang nói về một số bộ sưu tập nghiêm túc lớn. Chỉ cần sử dụng cái nào dễ đọc/duy trì hơn. – Hardwareguy

Trả lời

58

Nói chung Chậm hơn. Tổng số LINQ nói chung là hoạt động O(N) trong khi List.CountArray.Length đều được đảm bảo là O(1).

Tuy nhiên, một số trường hợp LINQ sẽ đặc biệt có thông số IEnumerable<T> bằng cách truyền tới một số loại giao diện nhất định như IList<T> hoặc ICollection<T>. Sau đó, nó sẽ sử dụng phương thức Đếm đó để thực hiện thao tác Count() thực tế. Vì vậy, nó sẽ quay trở lại xuống O(1). Nhưng bạn vẫn phải trả chi phí nhỏ cho cuộc gọi diễn viên và giao diện.

+0

@Marc, tôi đã thêm cavaet đó. – JaredPar

+0

Tôi không chắc chắn, nhưng tôi nghĩ rằng nếu List.Count() được chạy trên một IQueryable nó sẽ thực hiện lệnh select count (*) sql. nhưng nếu List.Count được chạy, nó sẽ liệt kê tất cả các mục và sau đó trả về số đếm. Nếu sau này, List.Count() sẽ nhanh hơn hầu hết thời gian. – Jose

+0

@Jared, Marcs trả lời là chính xác hơn, kiểm tra chỉ được thực hiện cho ICollection , mảng, HashSet, từ điển, danh sách, LinkedList và Queue tất cả thực hiện ICollection . Các lớp học System.Collection cũ không, nhưng sau đó một lần nữa không thực hiện IEnumerable anyway để họ không thể được sử dụng với LINQ. –

2

Tôi tin rằng nếu bạn gọi Linq.Count() trên ICollection hoặc IList (như ArrayList hoặc List) thì nó sẽ trả về giá trị của thuộc tính Count. Vì vậy, hiệu suất sẽ giống nhau về các bộ sưu tập đơn giản.

+0

ArrayList không phải là IEnumerable vì vậy bạn không thể thực thi các phương thức mở rộng LINQ trên đó. kiểm tra chỉ được thực hiện cho ICollection

25

Phương pháp Enumerable.Count() kiểm tra ICollection<T>, bằng cách sử dụng .Count - vì vậy trong trường hợp mảng và danh sách, nó không hiệu quả hơn nhiều (chỉ cần thêm một mức độ gián tiếp).

+0

Trên thực tế với mảng bạn nhận được 2 lớp indirection, xem câu trả lời của tôi: p –

2

Tôi sẽ nói điều đó tùy thuộc vào Danh sách. Nếu nó là một IQueryable là một bảng trong một db một nơi nào đó thì Count() sẽ là nhanh hơn nhiều bởi vì nó không phải tải tất cả các đối tượng. Nhưng nếu danh sách là trong bộ nhớ tôi sẽ đoán rằng tài sản Count sẽ nhanh hơn nếu không giống nhau.

22

Marc có câu trả lời đúng nhưng ma quỷ là chi tiết.

Trên máy tính của tôi:

  • Đối với mảng .Length là nhanh hơn so với Count()
  • Đối Lists Count là nhanh hơn so với Count() khoảng 10 lần khoảng 100 lần - Lưu ý: Tôi sẽ mong đợi hiệu suất tương tự từ tất cả các Bộ sưu tập thực hiện IList<T>

Mảng bắt đầu chậm hơn kể từ .Lĩnh vực chỉ liên quan đến một lớp vô hướng. Vì vậy .Count trên mảng bắt đầu chậm hơn 10x (trên máy tính của tôi), có thể là một trong những lý do đó giao diện được thực hiện một cách rõ ràng. Hãy tưởng tượng nếu bạn có một đối tượng với hai thuộc tính công khai, .Count và .Length. Cả hai đều làm điều tương tự nhưng .Count chậm hơn 10X.

Tất nhiên, điều này thực sự tạo ra nhiều sự khác biệt vì bạn sẽ phải đếm mảng của mình và liệt kê hàng triệu lần mỗi giây để cảm nhận hiệu suất.

Code:

static void TimeAction(string description, int times, Action func) { 
     var watch = new Stopwatch(); 
     watch.Start(); 
     for (int i = 0; i < times; i++) { 
      func(); 
     } 
     watch.Stop(); 
     Console.Write(description); 
     Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds); 
    } 

    static void Main(string[] args) { 
     var array = Enumerable.Range(0, 10000000).ToArray(); 
     var list = Enumerable.Range(0, 10000000).ToArray().ToList(); 

     // jit 
     TimeAction("Ignore and jit", 1 ,() => 
     { 
      var junk = array.Length; 
      var junk2 = list.Count; 
      array.Count(); 
      list.Count(); 
     }); 


     TimeAction("Array Length", 1000000,() => { 
      var tmp1 = array.Length; 
     }); 

     TimeAction("Array Count()", 1000000,() => 
     { 
      var tmp2 = array.Count(); 
     }); 

     TimeAction("Array Length through cast", 1000000,() => 
     { 
      var tmp3 = (array as ICollection<int>).Count; 
     }); 


     TimeAction("List Count", 1000000,() => 
     { 
      var tmp1 = list.Count; 
     }); 

     TimeAction("List Count()", 1000000,() => 
     { 
      var tmp2 = list.Count(); 
     }); 

     Console.ReadKey(); 
    } 

Kết quả:

 
Array Length Time Elapsed 3 ms 
Array Count() Time Elapsed 264 ms 
Array Length through cast Time Elapsed 16 ms 
List Count Time Elapsed 3 ms 
List Count() Time Elapsed 18 ms 
+0

May mắn 'collection.Count/Length' là dễ đọc hơn' collection.Count() '. Những trường hợp hiếm hoi mà mã đẹp hơn là hiệu quả hơn: P – nawfal

+0

Chỉ cần FYI, tôi thấy một sự khác biệt nhỏ giữa '(mảng như ICollection ) .Count;' và '(mảng như ICollection) .Count;' (ủng hộ của cựu). – jmoreno

0

Một số thêm thông tin - LINQ Đếm - sự khác biệt giữa việc sử dụng nó và không thể khổng lồ - và điều này không phải là qua ' bộ sưu tập lớn. Tôi có một bộ sưu tập được hình thành từ linq cho các đối tượng với khoảng 6500 mục (lớn .. nhưng không lớn bằng bất kỳ phương tiện nào). Count() trong trường hợp của tôi mất vài giây. Chuyển đổi thành một danh sách (hoặc mảng, whatver) số lượng là gần như ngay lập tức. Việc đếm số này trong vòng lặp bên trong có nghĩa là tác động có thể rất lớn. Đếm đếm qua tất cả mọi thứ.Một mảng và một danh sách đều là 'tự nhận thức' về độ dài của chúng và không cần liệt kê chúng. Bất kỳ câu lệnh debug nào (log4net cho ex) tham chiếu đến số đếm này() cũng sẽ làm chậm mọi thứ xuống đáng kể. Làm cho bạn một ưu và nếu bạn cần phải tham khảo này thường tiết kiệm kích thước đếm và chỉ gọi nó một lần trên một bộ sưu tập LINQ trừ khi bạn chuyển đổi nó thành một danh sách và sau đó có thể tham khảo mà không có một hit hiệu suất.

Đây là bài kiểm tra nhanh về những gì tôi đã nói ở trên. Lưu ý mỗi khi chúng ta gọi Count() kích thước bộ sưu tập của chúng ta thay đổi .. do đó việc đánh giá diễn ra, nhiều hơn một hoạt động 'đếm' dự kiến. Chỉ cần một cái gì đó để nhận thức được:)

 
    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 

    namespace LinqTest 
    { 
     class TestClass 
     { 
      public TestClass() 
      { 
       CreateDate = DateTime.Now; 
      } 
      public DateTime CreateDate; 
     } 

     class Program 
     { 

      static void Main(string[] args) 
      { 
       //Populate the test class 
       List list = new List(1000); 
       for (int i=0; i<1000; i++) 
       { 
        System.Threading.Thread.Sleep(20); 
        list.Add(new TestClass()); 
        if(i%100==0) 
        { 
         Console.WriteLine(i.ToString() + " items added"); 
        } 
       } 

       //now query for items 
       var newList = list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now); 
       while (newList.Count() > 0) 
       { 
        //Note - are actual count keeps decreasing.. showing our 'execute' is running every time we call count. 
        Console.WriteLine(newList.Count()); 
        System.Threading.Thread.Sleep(500); 
       } 
      } 
     } 
    } 
 
+2

list.Where trả về một IEnumerable để bạn không có phím tắt với Count() ... nếu bạn thực hiện nó, bạn sẽ thấy perf khá tốt (ví dụ: 'list.Where (o => o.CreateDate.AddSeconds (5)> DateTime .Now) .ToList() ') –

Các vấn đề liên quan