LINQ के साथ लिस्ट खाली होने पर चेक करना


122

यदि सूची खाली है, तो यह निर्धारित करने के लिए "सर्वोत्तम" (गति और पठनीयता दोनों को ध्यान में रखते हुए) तरीका क्या है? भले ही सूची प्रकार की हो IEnumerable<T>और उसके पास काउंट प्रॉपर्टी न हो।

अभी मैं इसके बीच में उछाल रहा हूं:

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

5
बहुत अधिक बेहतर मिश्रण isऔर castउपयोग नहीं करते हैं asऔर nullICollection<T> collection = list as ICollection<T>; if (collection != null) return colllection.Count;
जांचते हैं

2
एक अतिरिक्त विधि क्यों लिखें? के list.Any()बराबर नहीं है list.IsEmpty? फ्रेमवर्क विधि को अनुकूलित किया जाना चाहिए - यह केवल एक नया लिखने के लायक है अगर आपको यह पता चला है कि यह एक आदर्श अड़चन है।
dbkk

6
क्या किसी ने उनके सुझाए गए कार्यान्वयन पर प्रदर्शन को मापने की जहमत उठाई या हर कोई सिर्फ विचारों को निकाल रहा है?
माइकल ब्राउन

मैंने .NET कोर क्लास लाइब्रेरी के लिए इश्यू का सुझाव दिया है जो IsEmptyएक्सटेंशन मेथड जोड़ता है । github.com/dotnet/corefx/issues/35054 कृपया जाँचें और वोट करें अगर आपको पसंद है और सहमत हैं।
RyotaMurohoshi

जवाबों:


100

आप ऐसा कर सकते हैं:

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

संपादित करें : ध्यान दें कि केवल .Count पद्धति का उपयोग तेजी से होगा यदि अंतर्निहित स्रोत में वास्तव में तेजी से गिनती संपत्ति है। ऊपर एक वैध अनुकूलन कुछ आधार प्रकारों का पता लगाने और केवल .Any () दृष्टिकोण के बजाय उन लोगों की संपत्ति का उपयोग करने के लिए किया जाएगा, लेकिन फिर वापस। किसी भी गारंटी नहीं की जा सकती है।


4
या एक पंक्ति का उपयोग करें और वापसी करें (स्रोत == अशक्त)? सच:; source.Any (); (यदि आपका कोई अपवाद नहीं फेंक रहा है)
Gage

1
मैं कहूंगा, हाँ, अशक्त के लिए एक अपवाद फेंक दें, लेकिन फिर एक दूसरी एक्सटेंशन विधि जोड़ें IsNullOrEmpty()
देवकर

1
सार्वजनिक स्थैतिक बूलियन IsNullOrEmpty <T> (यह IEnumerable <T> स्रोत) {वापसी स्रोत == अशक्त || ! source.Any (); }
दान

1
@Gage आजकल:return !source?.Any() ?? true;
रिक्शा

@ricksmt अपडेट के लिए धन्यवाद! मैं निश्चित रूप से उपयोग कर रहा हूँ!
गेज

14

आपके द्वारा तय किए गए कोड को मैं एक छोटा जोड़ दूंगा: इसके लिए भी जांच करें 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();
}

1
मुझे यह उत्तर पसंद है। चेतावनी का एक शब्द यह है कि कुछ संग्रह अपवादों को फेंक देंगे जब वे पूरी तरह से एक इंटरफ़ेस को लागू नहीं करते हैं NotSupportedExceptionया NotImplementedException। मैंने पहली बार आपके कोड उदाहरण का उपयोग किया था जब मुझे पता चला कि मैं एक संग्रह का उपयोग कर रहा था, जो काउंट (जो जानता था ...) के लिए एक अपवाद का उपयोग कर रहा था।
सैम

1
मैं समझता हूं कि इस तरह के अनुकूलन काउंट () जैसे तरीकों के लिए उपयोगी है, जिन्हें सभी तत्वों की गणना करने की आवश्यकता है। लेकिन किसी भी () को केवल एक ही तत्व में गणना करने की आवश्यकता है, इसलिए मैं यहां बिंदु को देखने में विफल हूं। दूसरी ओर, आपके द्वारा जोड़े जा रहे कास्ट्स और अगर-स्टेटमेंट्स एक निश्चित लागत है तो आपको हर कॉल पर भुगतान करना होगा।
कोडीनिक्स

8

LINQ खुद किसी तरह काउंट () विधि के आसपास कुछ गंभीर अनुकूलन कर रहा होगा।

क्या यह आपको आश्चर्यचकित करता है? मुझे लगता है कि IListकार्यान्वयन के लिए, Countबस विधि Anyको क्वेरी करना पड़ता है जबकि सीधे तत्वों की संख्या पढ़ता है IEnumerable.GetEnumerator, एक उदाहरण बनाएं और MoveNextकम से कम एक बार कॉल करें ।

/ EDIT @Matt:

मैं केवल यह मान सकता हूं कि IEnumerable के लिए गणना () विस्तार विधि कुछ इस तरह कर रही है:

हां, जरूर करता है। यही मेरा मतलब है। दरअसल, यह ICollectionइसके बजाय उपयोग करता है IListलेकिन परिणाम समान है।


6

मैंने अभी एक त्वरित परीक्षण लिखा है, यह कोशिश करें:

 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+ ऑब्जेक्ट्स को सूची में रखा और गिनती अभी भी तेज थी, अन्य प्रकारों के विपरीत)


12
Enumerable.Count<T>()के लिए विशेष हैंडलिंग है ICollection<T>। आप कुछ के साथ इस प्रयास करते हैं तो अन्य एक बुनियादी सूची की तुलना में, मैं उम्मीद आप देखेंगे काफी अलग (धीमा) का परिणाम है। Any()हालांकि उसी के बारे में रहेगा।
मार्क Gravell

2
मुझे मार्क के साथ बैठना है; यह वास्तव में उचित परीक्षा नहीं है।
दान ताओ

किसी भी विचार क्यों विशेष हैंडलिंग के लिए नहीं है Enumerable.Any<T>()के लिए ICollection<T>? निश्चित रूप से पैरामीटरहीन भी संपत्ति की Any()जांच कर सकता है? CountICollection<T>
लुकाज़ोइड

5

List.CountMicrosoft के दस्तावेज़ के अनुसार O (1) है:
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

तो बस List.Count == 0एक क्वेरी की तुलना में बहुत तेजी से उपयोग करें

ऐसा इसलिए है क्योंकि इसमें एक डेटा मेंबर होता है जिसे काउंट कहा जाता है जिसे किसी भी समय अपडेट किया जाता है या सूची में से कुछ जोड़ा जाता है, इसलिए जब आप कॉल List.Countकरते हैं तो इसे प्राप्त करने के लिए हर एलिमेंट के माध्यम से पुनरावृति नहीं करनी होती है, यह डेटा सदस्य को वापस कर देता है।


1
अगर यह "IEnumerable" है तो नहीं। (शुरुआत के लिए IEnumerable के पास "गणना" संपत्ति नहीं है, इसकी गणना () विधि है।)। "काउंट ()" को कॉल करने के लिए सूची में हर एक तत्व की जांच करने के लिए IEnumerable की आवश्यकता होगी। जबकि "कोई भी" जैसे ही 1 तत्व मिलेगा, वैसे ही वापस आ जाएगा।
00jt

यह डेटा स्रोत पर निर्भर करता है। यदि आप एक IEnumerable बनाने के लिए उपज का उपयोग करते हैं, तो यह आकार जानने के लिए IEnumerable को पीछे करना होगा। तो यह कुछ मामलों में केवल O (1) है। यह हमेशा O (1) नहीं होता है।
TamusJRoyce

3

यदि आपके पास कई आइटम हैं तो दूसरा विकल्प ज्यादा तेज है।

  • Any() 1 आइटम मिलते ही रिटर्न।
  • Count() पूरी सूची से गुजरते रहना है।

उदाहरण के लिए मान लीजिए कि गणन में 1000 वस्तुएं थीं।

  • Any() पहले एक की जाँच करेगा, फिर सच लौटाएगा।
  • Count() पूरे एन्यूमरेशन को ट्रेस करने के बाद 1000 लौटेंगे।

यह संभावित रूप से बदतर है यदि आप किसी एक ओवरराइड का उपयोग करते हैं - काउंट () को अभी भी हर एक आइटम की जांच करनी है, यहां तक ​​कि यह केवल एक मैच है।

आपको किसी भी एक का उपयोग करने की आदत है - यह समझ में आता है और पठनीय है।

एक कैविएट - यदि आपके पास एक IEnumerable के बजाय एक सूची है, तो उस सूची की गणना संपत्ति का उपयोग करें।


Any () और Count () के बीच का अंतर स्पष्ट लगता है, लेकिन @ crucible की प्रोफाइलिंग कोड से संकेत मिलता है कि IEnumerable <T> के कुछ कार्यान्वयन के लिए काउंट () तेज़ है। सूची के लिए <T> मैं हजारों आइटमों में सूची आकार ऊपर होने तक काउंट () से अधिक तेज़ परिणाम देने के लिए कोई भी () प्राप्त नहीं कर सकता। LINQ खुद किसी तरह काउंट () विधि के आसपास कुछ गंभीर अनुकूलन कर रहा होगा।
मैट हैमिल्टन

3

@ मुझे क्या आश्चर्य है कि मेरे परीक्षणों में, मैं सूची को एक ऐसी विधि में पारित कर रहा हूं, जो स्वीकार करती है IEnumerable<T>, इसलिए रनटाइम काउंट () के लिए एक्सटेंशन पद्धति को कॉल करके इसे अनुकूलित नहीं कर सकता है IList<T>

मैं केवल यह मान सकता हूं कि 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>

/ EDIT @Konrad +1 मेट - आप इसके बारे में सही हैं कि इसकी अधिक संभावना है ICollection<T>


1

ठीक है, तो इस बारे में क्या?

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

संपादित करें: मैंने महसूस किया है कि किसी ने इस समाधान को पहले ही छोड़ दिया है। यह उल्लेख किया गया था कि कोई भी () विधि ऐसा करेगी, लेकिन स्वयं क्यों नहीं? सादर


3
लेकिन यह तब कम हो जाता है जब आप इसे एक usingब्लॉक में ठीक से संलग्न करते हैं, अन्यथा आपने एक IDisposableऑब्जेक्ट का निर्माण किया है और फिर उसे छोड़ दिया है। फिर, निश्चित रूप से, यह तब और अधिक सरल हो जाता है जब आप उस विस्तार विधि का उपयोग करते हैं जो पहले से ही है और बस इसे बदल दें return !enumerable.Any()(जो ठीक यही करता है)।
डान ताओ

पहले से मौजूद पद्धति को फिर से लिखना क्यों? जैसा कि उल्लेख किया गया Any()है, ठीक उसी तरह से, इसलिए किसी अन्य नाम के साथ ठीक उसी विधि को जोड़ना सिर्फ भ्रमित करने वाला होगा।
जुलिएन एन

1

एक अन्य विचार:

if(enumerable.FirstOrDefault() != null)

हालांकि मुझे कोई भी () दृष्टिकोण अधिक पसंद है।


3
क्या होगा यदि आपके पास एक गैर-खाली सूची है जिसमें पहला तत्व शून्य है?
एकेवू

1

एंटिटी फ्रेमवर्क के साथ काम करने के लिए इसे प्राप्त करना महत्वपूर्ण था:

var genericCollection = list as ICollection<T>;

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

कैसे इस सवाल का जवाब देता है? इसके अंदर कोई तत्व नहीं होने पर संग्रह शून्य नहीं हो सकता।
मार्टिन वेरजन्स

0

अगर मैं काउंट के साथ जांच करता हूं () लाइनक डेटाबेस में "सेलेक्ट कॉउंट (*) .." निष्पादित करता है, लेकिन मुझे यह जांचने की जरूरत है कि क्या परिणाम डेटा में हैं, मैंने काउंट () के बजाय फर्स्टऑर्डफॉल्ट () शुरू करने का संकल्प लिया;

इससे पहले

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)
{
    //....
}

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

यहाँ दान ताओ के उत्तर का मेरा कार्यान्वयन है, जो एक विधेय के लिए अनुमति देता है:

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। बस इतना ही


1
यह एक भयानक विचार है। ToList () का उपयोग नहीं किया जाना चाहिए, क्योंकि यह गणना करने योग्य को पूरी तरह से मूल्यांकन करने के लिए मजबूर करता है। इसके बजाय .Any () का उपयोग करें।
जॉन री

-5

यह विस्तार विधि मेरे लिए काम करती है:

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

5
अपवादों के ऐसे उपयोग से बचें। उपरोक्त कोड में, आप कुछ निश्चित, अच्छी तरह से परिभाषित इनपुट (यानी खाली गणना) के लिए एक अपवाद की अपेक्षा करते हैं। इसलिए, वे कोई अपवाद नहीं हैं, वे नियम हैं। यह इस नियंत्रण तंत्र का दुरुपयोग है जिसका पठनीयता और प्रदर्शन पर प्रभाव पड़ता है। वास्तव में असाधारण मामलों के लिए अपवादों के उपयोग को आरक्षित करें।
कोनराड रुडोल्फ

आम तौर पर, मैं सहमत होता। लेकिन यह एक संबंधित लापता IsEmpty विधि के लिए एक समाधान है। और मेरा तर्क है कि कुछ करने का आदर्श तरीका कभी नहीं होता है ... इसके अलावा, विशेष रूप से इस मामले में, इरादा बहुत स्पष्ट है और "गंदे" कोड को संक्षिप्त कर दिया गया है और एक अच्छी तरह से परिभाषित जगह में छिपा हुआ है।
जॉनी डी

3
-1: यदि आप इसे इस तरह से करना चाहते हैं, तो FirstOrDefault () का उपयोग करें, जैसा कि चुलियोमार्टीनज़ के उत्तर में है।
डैनियल रोज

3
अपवाद हैंडलिंग में वास्तव में खराब प्रदर्शन दक्षता है। तो यह यहाँ सबसे खराब समाधान हो सकता है।
जुलिएन एन

"अपवाद असाधारण होना चाहिए।" - सामान्य कार्यक्रम प्रवाह के लिए उनका उपयोग न करें।
जॉन री
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.