التحقق مما إذا كانت القائمة فارغة باستخدام LINQ

StackOverflow https://stackoverflow.com/questions/41319

  •  09-06-2019
  •  | 
  •  

سؤال

ما هي الطريقة "الأفضل" (مع مراعاة السرعة وسهولة القراءة) لتحديد ما إذا كانت القائمة فارغة؟حتى لو كانت القائمة من النوع IEnumerable<T> وليس لديه خاصية Count.

وأنا الآن أتأرجح بين هذا:

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

وهذا:

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

أعتقد أن الخيار الثاني هو الأسرع، لأنه سيعود بنتيجة بمجرد رؤية العنصر الأول، في حين أن الخيار الثاني (بالنسبة لـ IEnumerable) سيحتاج إلى زيارة كل عنصر لإرجاع العدد.

ومع ذلك، هل يبدو الخيار الثاني قابلاً للقراءة بالنسبة لك؟أيهم تفضل؟أو هل يمكنك التفكير في طريقة أفضل لاختبار القائمة الفارغة؟

يحرر يبدو أن استجابة @lassevk هي الأكثر منطقية، إلى جانب القليل من التحقق من وقت التشغيل لاستخدام عدد مخبأ إن أمكن، مثل هذا:

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

    return !list.Any();
}
هل كانت مفيدة؟

المحلول

يمكنك القيام بذلك:

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

يحرر:لاحظ أن استخدام الأسلوب .Count سيكون سريعًا إذا كان المصدر الأساسي يحتوي بالفعل على خاصية Fast Count.سيكون التحسين الصحيح أعلاه هو اكتشاف بعض الأنواع الأساسية واستخدام خاصية .Count لتلك الأنواع ببساطة، بدلاً من طريقة .Any()، ولكن بعد ذلك الرجوع إلى .Any() إذا لم يكن من الممكن تقديم أي ضمان.

نصائح أخرى

أود أن أقوم بإضافة صغيرة إلى الكود الذي يبدو أنك استقرت عليه:تحقق أيضًا من ICollection, ، حيث يتم تنفيذ ذلك حتى من خلال بعض الفئات العامة غير القديمة أيضًا (على سبيل المثال، Queue<T> و Stack<T>).وأود أن تستخدم أيضا as بدلاً من is لأنها أكثر اصطلاحية و وقد ثبت أنه أسرع.

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

يجب أن تقوم LINQ نفسها ببعض التحسينات الجادة حول طريقة Count() بطريقة ما.

هل هذا يفاجئك؟أتصور ذلك ل IList التنفيذ, Count ببساطة يقرأ عدد العناصر مباشرة بينما Any لديه للاستعلام عن IEnumerable.GetEnumerator الطريقة، إنشاء مثيل والاتصال MoveNext مرة على الاقل.

/تحريرمات:

لا يمكنني إلا أن أفترض أن طريقة ملحق Count() لـ IEnumerable تقوم بشيء مثل هذا:

نعم، بالطبع لا.هذا ما قصدته.في الواقع، فإنه يستخدم ICollection بدلاً من IList ولكن النتيجة واحدة.

لقد كتبت للتو اختبارًا سريعًا، جرب هذا:

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

والثاني أبطأ بثلاث مرات تقريبًا :)

إن محاولة اختبار ساعة الإيقاف مرة أخرى باستخدام المكدس أو المصفوفة أو سيناريوهات أخرى، يعتمد الأمر حقًا على نوع القائمة التي تبدو عليها - لأنها تثبت أن العد أبطأ.

لذا أعتقد أن ذلك يعتمد على نوع القائمة التي تستخدمها!

(فقط للإشارة إلى أنني وضعت أكثر من 2000 كائن في القائمة وكان العدد لا يزال أسرع، على عكس الأنواع الأخرى)

List.Count هو O(1) وفقًا لوثائق Microsoft:
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

لذلك استخدم فقط List.Count == 0 إنه أسرع بكثير من الاستعلام

وذلك لأنه يحتوي على عضو بيانات يسمى Count والذي يتم تحديثه في أي وقت تتم فيه إضافة شيء ما أو إزالته من القائمة، لذلك عند الاتصال List.Count ليس من الضروري التكرار خلال كل عنصر للحصول عليه، بل يقوم فقط بإرجاع عضو البيانات.

الخيار الثاني أسرع بكثير إذا كان لديك عناصر متعددة.

  • Any() يعود بمجرد العثور على عنصر واحد.
  • Count() يجب أن يستمر في تصفح القائمة بأكملها.

على سبيل المثال لنفترض أن التعداد يحتوي على 1000 عنصر.

  • Any() سوف تحقق أول واحد، ثم يعود صحيحا.
  • Count() سيعود 1000 بعد اجتياز التعداد بأكمله.

من المحتمل أن يكون هذا أسوأ إذا كنت تستخدم أحد تجاوزات المسند - لا يزال يتعين على Count() التحقق من كل عنصر على حدة، حتى لو كان هناك تطابق واحد فقط.

تعتاد على استخدام Any one - فهي منطقية ويمكن قراءتها.

تحذير واحد - إذا كان لديك قائمة، بدلاً من IEnumerable فقط، فاستخدم خاصية Count الخاصة بتلك القائمة.

@ Konrad ما أدهشني هو أنني في اختباراتي أقوم بتمرير القائمة إلى طريقة تقبل IEnumerable<T>, ، لذلك لا يمكن لوقت التشغيل تحسينه عن طريق استدعاء طريقة الامتداد Count() لـ IList<T>.

لا يمكنني إلا أن أفترض أن طريقة ملحق Count() لـ IEnumerable تقوم بشيء مثل هذا:

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

...بمعنى آخر، القليل من تحسين وقت التشغيل للحالة الخاصة لـ IList<T>.

/ تحرير @ Konrad +1 يا صديقي - أنت على حق بشأن أنه من المرجح أن تكون قيد التشغيل ICollection<T>.

حسنًا، ماذا عن هذا؟

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

يحرر:لقد أدركت للتو أن شخصًا ما قد رسم هذا الحل بالفعل.لقد ذكرنا أن طريقة Any() ستفعل ذلك، لكن لماذا لا تفعل ذلك بنفسك؟يعتبر

فكرة اخرى:

if(enumerable.FirstOrDefault() != null)

ومع ذلك فأنا أحب أسلوب Any() أكثر.

كان هذا أمرًا بالغ الأهمية لجعل هذا يعمل مع Entity Framework:

var genericCollection = list as ICollection<T>;

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

إذا قمت بالتحقق من Count() Linq ينفذ "SELECT COUNT(*.." في قاعدة البيانات، ولكنني بحاجة للتحقق مما إذا كانت النتائج تحتوي على بيانات، فقد قررت تقديم FirstOrDefault() بدلاً من Count()؛

قبل

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

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

بعد

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

var itemCfop = cfop.FirstOrDefault();

if (itemCfop != null)
{
    //....
}
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);
    }

إليكم تطبيقي لإجابة دان تاو، مما يسمح بالمسند:

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;
}
List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;

myList.ToList().Count == 0.هذا كل شئ

طريقة التمديد هذه تناسبني:

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top