2008-09-03 18 views
121

Cách "tốt nhất" (lấy cả tốc độ và khả năng đọc) là gì để xác định xem danh sách có trống không? Ngay cả khi danh sách là loại IEnumerable<T> và không có thuộc tính Đếm.Kiểm tra xem danh sách có trống không với LINQ

Ngay bây giờ tôi đang tung tăng giữa này:

if (myList.Count() == 0) { ... } 

và điều này:

if (!myList.Any()) { ... } 

đoán của tôi là sự lựa chọn thứ hai là nhanh hơn, vì nó sẽ trở lại với một kết quả ngay sau khi nó thấy mục đầu tiên, trong khi tùy chọn thứ hai (cho một IEnumerable) sẽ cần phải truy cập mọi mục để trả về số đếm.

Điều đó đang được nói, tùy chọn thứ hai có thể đọc được không? Bạn thích cái nào? Hoặc bạn có thể nghĩ ra một cách tốt hơn để kiểm tra danh sách trống không?

Sửa @ phản ứng lassevk của có vẻ là hợp lý nhất, cùng với một chút thời gian chạy kiểm tra để sử dụng một số lượng lưu trữ nếu có thể, như thế này:

public static bool IsEmpty<T>(this IEnumerable<T> list) 
{ 
    if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0; 

    return !list.Any(); 
} 
+5

Tốt hơn nhiều không trộn 'is' và' cast' nhưng sử dụng 'as' và' null' kiểm tra: 'ICollection collection = list as ICollection ; if (collection! = null) return colllection.Count; ' – abatishchev

+2

Tại sao viết một phương thức bổ sung? Không phải là 'list.Any()' tương đương với 'list.IsEmpty'? Phương pháp khung phải được tối ưu hóa - bạn chỉ nên viết một phương pháp mới nếu bạn đã tìm ra đó là một nút cổ chai hoàn hảo. – dbkk

+6

Có ai bận tâm để đo lường hiệu suất trên các triển khai được đề xuất của họ hay là mọi người chỉ ném ra các ý tưởng? –

Trả lời

93

Bạn có thể làm điều này:

public static Boolean IsEmpty<T>(this IEnumerable<T> source) 
{ 
    if (source == null) 
     return true; // or throw an exception 
    return !source.Any(); 
} 

Chỉnh sửa: Lưu ý rằng chỉ cần sử dụng phương thức .Count sẽ nhanh nếu nguồn cơ bản thực sự có thuộc tính Đếm nhanh. Một tối ưu hóa hợp lệ ở trên sẽ là phát hiện một vài loại cơ sở và chỉ cần sử dụng thuộc tính .Count của chúng, thay vì phương thức .Any(), nhưng sau đó quay trở lại .Any() nếu không bảo đảm có thể được thực hiện.

+4

Hoặc sử dụng một dòng và trả về (nguồn == null)? true:! source.Any(); (Nếu bạn không ném một ngoại lệ) – Gage

+1

Tôi sẽ nói, có, ném một ngoại lệ cho null, nhưng sau đó thêm một phương thức mở rộng thứ hai được gọi là 'IsNullOrEmpty()'. – devuxer

+1

công cộng tĩnh Boolean IsNullOrEmpty (IEnumerable nguồn này) { return source == null || source.Any(); } – dan

6

Tôi chỉ viết lên một thử nghiệm nhanh, hãy thử này:

IEnumerable<Object> myList = new List<Object>(); 

Stopwatch watch = new Stopwatch(); 

int x; 

watch.Start(); 
for (var i = 0; i <= 1000000; i++) 
{ 
    if (myList.Count() == 0) x = i; 
} 
watch.Stop(); 

Stopwatch watch2 = new Stopwatch(); 

watch2.Start(); 
for (var i = 0; i <= 1000000; i++) 
{ 
    if (!myList.Any()) x = i; 
} 
watch2.Stop(); 

Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString()); 
Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString()); 
Console.ReadLine(); 

Thứ hai là gần gấp ba lần chậm hơn :)

Cố gắng kiểm tra đồng hồ bấm giờ một lần nữa với một Stack hoặc mảng hoặc các kịch bản khác, nó thực sự phụ thuộc vào loại danh sách có vẻ như - bởi vì họ chứng minh Count chậm hơn.

Vì vậy, tôi đoán nó phụ thuộc vào loại danh sách bạn đang sử dụng!

(Chỉ cần chỉ ra, tôi đặt 2000+ đối tượng trong danh sách và số lượng vẫn còn nhanh hơn, trái ngược với các loại khác)

+10

'Enumerable.Count ()' có xử lý đặc biệt cho 'ICollection '. Nếu bạn thử điều này với một cái gì đó * khác * hơn một danh sách cơ bản, tôi hy vọng bạn sẽ thấy * đáng kể * kết quả khác nhau (chậm hơn). 'Any()' sẽ vẫn như cũ. –

+2

Tôi phải đồng ý với Marc; đây không phải là một thử nghiệm thực sự công bằng. –

+0

Bất kỳ ý tưởng nào tại sao không có xử lý đặc biệt cho 'Enumerable.Any ()' cho 'ICollection '? chắc chắn parameterless 'Any()' chỉ có thể kiểm tra thuộc tính 'Count' cho' ICollection 'quá? – Lukazoid

2

Lựa chọn thứ hai là nhanh hơn nhiều nếu bạn có nhiều mục.

  • Any() trả về ngay khi tìm thấy 1 mục.
  • Count() phải tiếp tục xem toàn bộ danh sách.

Ví dụ giả sử điều tra có 1000 mục.

  • Any() sẽ kiểm tra giá trị đầu tiên, sau đó trả về giá trị true.
  • Count() sẽ trả về 1000 sau khi vượt qua toàn bộ điều tra.

Điều này có khả năng tồi tệ hơn nếu bạn sử dụng một trong các ghi đè vị trí - Đếm() vẫn phải kiểm tra từng mục, ngay cả khi chỉ có một kết quả phù hợp.

Bạn quen với việc sử dụng Bất kỳ cái nào - nó có ý nghĩa và có thể đọc được.

Một báo trước - nếu bạn có một Danh sách, thay vì chỉ là một IEnumerable thì hãy sử dụng thuộc tính Đếm của danh sách đó.

+0

Sự khác biệt giữa Bất kỳ() và Đếm() có vẻ rõ ràng, nhưng mã hồ sơ của @ crucible dường như chỉ ra rằng Count() nhanh hơn đối với một số triển khai nhất định của IEnumerable . Đối với Danh sách Tôi không thể nhận được Bất kỳ() để cung cấp kết quả nhanh hơn Count() cho đến khi kích thước danh sách nằm trong hàng nghìn mục. LINQ chính nó phải làm một số tối ưu hóa nghiêm trọng xung quanh phương thức Count() bằng cách nào đó. –

8

Bản thân LINQ phải thực hiện một số tối ưu hóa nghiêm túc xung quanh phương thức Count() bằng cách nào đó.

Điều này làm bạn ngạc nhiên? Tôi tưởng tượng rằng để triển khai IList, Count chỉ cần đọc số lượng phần tử trực tiếp trong khi Any phải truy vấn phương thức IEnumerable.GetEnumerator, tạo một phiên bản và gọi MoveNext ít nhất một lần.

/EDIT @ Matt:

Tôi chỉ có thể giả định rằng Count() phương pháp khuyến nông cho IEnumerable đang làm một cái gì đó như thế này:

Vâng, tất nhiên nó. Đây là ý tôi. Trên thực tế, nó sử dụng ICollection thay vì IList nhưng kết quả là như nhau.

3

@Konrad điều khiến tôi ngạc nhiên là tôi đang chuyển danh sách thành phương thức chấp nhận IEnumerable<T>, do đó thời gian chạy không thể tối ưu hóa nó bằng cách gọi phương thức mở rộng Count() cho IList<T>.

Tôi chỉ có thể giả định rằng Count() phương pháp khuyến nông cho IEnumerable đang làm một cái gì đó như thế này:

public static int Count<T>(this IEnumerable<T> list) 
{ 
    if (list is IList<T>) return ((IList<T>)list).Count; 

    int i = 0; 
    foreach (var t in list) i++; 
    return i; 
} 

... hay nói cách khác, một chút của tối ưu hóa thời gian chạy cho các trường hợp đặc biệt của IList<T>.

/EDIT @Konrad +1 bạn đời - bạn nói đúng về khả năng là trên ICollection<T>.

-5

phương pháp Phần mở rộng này làm việc cho tôi:

public static bool IsEmpty<T>(this IEnumerable<T> enumerable) 
{ 
    try 
    { 
     enumerable.First(); 
     return false; 
    } 
    catch (InvalidOperationException) 
    { 
     return true; 
    } 
} 
+5

Tránh sử dụng ngoại lệ như vậy. Trong đoạn mã trên, bạn * mong đợi * một ngoại lệ đối với các đầu vào nhất định, được xác định rõ ràng (tức là các liệt kê trống). Do đó, chúng không là ngoại lệ, chúng là quy luật. Đó là sự lạm dụng cơ chế kiểm soát này có tác động đến khả năng đọc và hiệu suất. Dự trữ việc sử dụng ngoại lệ cho các trường hợp thực sự đặc biệt. –

+0

Nói chung, tôi đồng ý. Nhưng đây là giải pháp cho phương thức IsEmpty thiếu tương ứng. Và tôi cho rằng cách giải quyết không bao giờ là cách lý tưởng để làm điều gì đó ... Hơn nữa, đặc biệt trong trường hợp này, ý định rất rõ ràng và mã "bẩn" được đóng gói và ẩn đi trong một nơi được xác định rõ. –

+3

-1: Nếu bạn muốn thực hiện theo cách này, hãy sử dụng FirstOrDefault(), như trong câu trả lời của ChulioMartinez. –

1

Ok, vì vậy những gì về việc này không?

public static bool IsEmpty<T>(this IEnumerable<T> enumerable) 
{ 
    return !enumerable.GetEnumerator().MoveNext(); 
} 

EDIT: Tôi vừa nhận ra rằng ai đó đã phác thảo giải pháp này rồi. Nó đã được đề cập rằng phương thức Any() sẽ làm điều này, nhưng tại sao bạn không tự làm điều đó? Trân trọng

+3

NHƯNG nó trở nên ít gọn gàng hơn khi bạn đưa nó vào một khối 'using' đúng cách vì bạn đã xây dựng một đối tượng' IDisposable' và sau đó bỏ nó. Sau đó, tất nhiên, nó trở thành * nhiều hơn * gọn gàng khi bạn sử dụng phương thức mở rộng đã có và chỉ cần thay đổi nó thành 'return! Enumerable.Any()' (chính xác là điều này). –

+0

Tại sao lại viết lại một phương thức đã tồn tại? Như đã đề cập, 'Any()' thực hiện chính xác điều đó, vì vậy việc thêm chính xác cùng một phương thức với tên khác sẽ chỉ gây nhầm lẫn. –

1

Một ý tưởng:

if(enumerable.FirstOrDefault() != null) 

Tuy nhiên tôi thích Bất kỳ) (phương pháp hơn.

+1

Điều gì sẽ xảy ra nếu bạn có một danh sách không trống trong đó phần tử đầu tiên là null? – Ekevoo

3

List.Count là O (1) theo tài liệu của Microsoft:
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

vì vậy chỉ cần sử dụng List.Count == 0 nó nhanh hơn nhiều so với một truy vấn

Đây là bởi vì nó có một thành viên dữ liệu được gọi Đếm được cập nhật bất kỳ thời gian một cái gì đó được thêm vào hoặc loại bỏ khỏi danh sách, vì vậy khi bạn gọi List.Count nó không phải lặp qua tất cả các phần tử để có được nó, nó chỉ trả về thành viên dữ liệu.

+1

nếu đó là "IEnumerable" thì không. (cho người mới bắt đầu IEnumerable không có thuộc tính "Đếm", nó có phương thức Count().). Việc gọi "Count()" sẽ yêu cầu IEnumerable kiểm tra mọi phần tử trong danh sách. Trong khi "Bất kỳ" sẽ chỉ trở lại ngay sau khi nó tìm thấy 1 phần tử. – 00jt

+0

Nó phụ thuộc vào nguồn dữ liệu. Nếu bạn sử dụng năng suất để xây dựng một IEnumerable, nó sẽ phải đi qua IEnumerable để biết kích thước của nó. Vì vậy, nó chỉ là O (1) trong một số trường hợp. Nó không phải luôn luôn là O (1). – TamusJRoyce

14

Tôi sẽ thêm một chút vào mã mà bạn dường như đã giải quyết: kiểm tra cũng ICollection, vì điều này được thực hiện ngay cả với một số lớp chung không lỗi thời (ví dụ: Queue<T>Stack<T>). Tôi cũng sẽ sử dụng as thay vì is vì nó thành ngữ hơn và has been shown to be faster.

public static bool IsEmpty<T>(this IEnumerable<T> list) 
{ 
    if (list == null) 
    { 
     throw new ArgumentNullException("list"); 
    } 

    var genericCollection = list as ICollection<T>; 
    if (genericCollection != null) 
    { 
     return genericCollection.Count == 0; 
    } 

    var nonGenericCollection = list as ICollection; 
    if (nonGenericCollection != null) 
    { 
     return nonGenericCollection.Count == 0; 
    } 

    return !list.Any(); 
} 
+1

Tôi thích câu trả lời này. Một cảnh báo là một số bộ sưu tập sẽ ném ngoại lệ khi chúng không thực hiện đầy đủ một giao diện như 'NotSupportedException' hoặc' NotImplementedException'. Lần đầu tiên tôi sử dụng ví dụ mã của bạn khi tôi phát hiện ra một bộ sưu tập mà tôi đã sử dụng đã ném một ngoại lệ cho Count (ai biết ...). – Sam

0

Nếu tôi kiểm tra bằng Đếm() LINQ thực hiện "SELECT COUNT (*) .." trong cơ sở dữ liệu, nhưng tôi cần kiểm tra xem kết quả có chứa dữ liệu hay không, tôi đã quyết định giới thiệu FirstOrDefault() thay vì Đếm();

Trước

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>() 

if (cfop.Count() > 0) 
{ 
    var itemCfop = cfop.First(); 
    //.... 
} 

Sau

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>() 

var itemCfop = cfop.FirstOrDefault(); 

if (itemCfop != null) 
{ 
    //.... 
} 
1

này là rất quan trọng để có được điều này để làm việc với Entity Framework:

var genericCollection = list as ICollection<T>; 

if (genericCollection != null) 
{ 
    //your code 
} 
+0

Làm cách nào để trả lời câu hỏi? bộ sưu tập có thể không rỗng trong khi không có phần tử bên trong. –

0
private bool NullTest<T>(T[] list, string attribute) 

    { 
     bool status = false; 
     if (list != null) 
     { 
      int flag = 0; 
      var property = GetProperty(list.FirstOrDefault(), attribute); 
      foreach (T obj in list) 
      { 
       if (property.GetValue(obj, null) == null) 
        flag++; 
      } 
      status = flag == 0 ? true : false; 
     } 
     return status; 
    } 


public PropertyInfo GetProperty<T>(T obj, string str) 

    { 
     Expression<Func<T, string, PropertyInfo>> GetProperty = (TypeObj, Column) => TypeObj.GetType().GetProperty(TypeObj 
      .GetType().GetProperties().ToList() 
      .Find(property => property.Name 
      .ToLower() == Column 
      .ToLower()).Name.ToString()); 
     return GetProperty.Compile()(obj, str); 
    } 
0
List<T> li = new List<T>(); 
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty; 
+1

Ít nhất hãy giải thích mã này làm gì. – quantum

0

Dưới đây là thực hiện của tôi về câu trả lời của Dan Tao, cho phép một vị:

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
{ 
    if (source == null) throw new ArgumentNullException(); 
    if (IsCollectionAndEmpty(source)) return true; 
    return !source.Any(predicate); 
} 

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source) 
{ 
    if (source == null) throw new ArgumentNullException(); 
    if (IsCollectionAndEmpty(source)) return true; 
    return !source.Any(); 
} 

private static bool IsCollectionAndEmpty<TSource>(IEnumerable<TSource> source) 
{ 
    var genericCollection = source as ICollection<TSource>; 
    if (genericCollection != null) return genericCollection.Count == 0; 
    var nonGenericCollection = source as ICollection; 
    if (nonGenericCollection != null) return nonGenericCollection.Count == 0; 
    return false; 
} 
-3

myList.ToList().Count == 0. Đó là tất cả

+1

Đây là một ý tưởng khủng khiếp. ToList() không nên bị lạm dụng, vì nó buộc đếm được để được đánh giá đầy đủ. Sử dụng .Any() để thay thế. –