2011-10-03 19 views
8

मैं LINQ से ऑब्जेक्ट्स का उपयोग कर रहा हूं और आश्चर्यचकित हूं कि मेरे पास एक इंडेक्स का उपयोग करके मेरे प्रश्नों के प्रदर्शन में सुधार करना संभव है। यह एक उदाहरण के साथ सबसे अच्छी तरह से समझाया गया है। एक सरल प्रकार ...ऑब्जेक्ट्स के साथ LINQ ऑब्जेक्ट्स और बेहतर पेर्फ?

public class Person 
{ 
    public int Age; 
    public string FirstName; 
    public string LastName; 
} 

और एक सरल प्रश्न मैं इसके खिलाफ होगा ...

List<Person> people = new List<Person>(); 

// 'people' populated with 50,000 instances... 

var x = from t in people 
     where t.Age > 18 && t.Age < 21 
     select t; 

कल्पना कीजिए अगर मैं सही ढंग तब से कहाँ विस्तार विधि करके बताना होगा कार्यान्वयन वस्तुओं के लिए LINQ को समझने वास्तव में मिलान करने वाले 100 को खोजने के लिए लोगों के संग्रह में सभी 50,000 उदाहरण। जैसा कि होता है मेरे पास पहले से ही लोगों के संग्रह की एक अनुक्रमणिका है जो आयु द्वारा क्रमबद्ध है। इस तरह ...

SortedList<int, Person> ageSorted = new SortedList<int, Person>(); 

जाहिर है यह समझ बनाने होगा अगर मैं कहाँ SortedList उपयोग करने के लिए मिल सकता है तो यह अब सभी 50,000 उदाहरणों की गणना करने में, बजाय 100 मिलान प्रविष्टियों की सीमा खोजने और इसलिए बचत है पहर।

यह मेरी स्थिति को सक्षम करने के वस्तुओं के लिए LINQ विस्तार करने के लिए संभव है? क्या यह पहले से ही संभव है लेकिन मुझे तकनीक याद आ रही है? i4o -

उत्तर

5

वहां पहले से ही एक परियोजना है जो मुझे लगता है कि वास्तव में करता है। मैं नहीं कह सकता कि मैंने इसे स्वयं इस्तेमाल किया है, लेकिन ऐसा लगता है कि आप जिस तरह की चीज चाहते हैं ... आपको अपने मौजूदा कोड को थोड़ा सा जोड़ना पड़ सकता है, लेकिन यह निश्चित रूप से देखने लायक है।

यदि सहायता नहीं है, तो आप कम से कम SortedList<TKey, TValue> पर अपने स्वयं के एक्सटेंशन विधियां लिख सकते हैं। आप शायद आसानी से अपने वास्तविक where खंड का उपयोग करने में सक्षम नहीं होंगे, लेकिन आप न्यूनतम और अधिकतम मूल्य लेने के अपने स्वयं के तरीकों का उपयोग कर सकते हैं। आप भी उन्हें IList<T> पर लागू करना चाहते हैं, जहां आप पर जोर दें कि आपने पहले ही मूल्यों को उचित रूप से क्रमबद्ध किया है (कुछ तुलनाकर्ता के अनुसार)।

उदाहरण (पूरी तरह से अपरीक्षित) के लिए

:

public static IEnumerable<T> Between<T, TKey>(this IList<T> source, 
               Func<T, TKey> projection, 
               TKey minKeyInclusive, 
               TKey maxKeyExclusive, 
               IComparer<TKey> comparer) 
{ 
    comparer = comparer ?? Comparer<TKey>.Default; 

    // TODO: Find the index of the lower bound via a binary search :) 
    // (It's too late for me to jot it down tonight :) 
    int index = ...; // Find minimum index 

    while (index < source.Count && 
      comparer.Compare(projection(source[index]), maxKeyExclusive) < 0) 
    { 
     yield return source[index]; 
     index++; 
    } 
} 

(आप केवल IList<T> के बजाय List<T> है, तो आप List<T>.BinarySearch इस्तेमाल कर सकते हैं, हालांकि आप एक कस्टम IComparer<T> का निर्माण करने की आवश्यकता होगी।)

इसके अलावा , SortedSet<T> पर .NET 4.

+0

धन्यवाद। वह निश्चित रूप से वह काम करेगा जो मुझे प्राप्त करने की उम्मीद थी। –

+0

@ फिलहाइट, @ जॉन स्केट एक 'List.BinarySearch' विधि है जिसका उपयोग उपरोक्त कोड में किया जा सकता है, विधि हस्ताक्षर के मामूली संशोधन के साथ। बीटीडब्लू, क्रमबद्ध सूची में एक बाइनरी खोज भी है: 'SortedList.IndexOfKey'। –

+0

बीटीडब्लू, 'सूची। बाइनरी सर्च' का उपयोग सटीक मिलान मौजूद नहीं होने पर निकटतम मैच खोजने के लिए किया जा सकता है। उत्सुकता से, 'SortedList.IndexOfKey' में यह क्षमता प्रतीत नहीं होती है। –

2

LINQ क्वेरी सिंटैक्स वास्तव में Where नामक किसी भी एक्सटेंशन विधि का उपयोग करता है जो हस्ताक्षर से मेल खाता है, तो आप अलवा कर सकते हैं ys अपना खुद का लिखें जो आपके विशिष्ट प्रकार को जिस तरह से आप चाहते हैं उसे संभालता है।

public static IEnumerable<Person> Where(this IEnumerable<Person> collection, Func<Person, bool> condition) 
    { 
     Console.WriteLine("My Custom 'Where' method called"); 
     return System.Linq.Enumerable.Where(collection, condition); 
    } 

...

 var x = from t in people 
       where t.Age > 18 && t.Age < 21 
       select t; //Will print "My Custom 'Where' method called" 

तो फिर तुम किसी भी तर्क आप चाहते हैं आवेदन कर सकते हैं। मेरा मानना ​​है कि सामान्य विधि ओवरलोड नियम यह निर्धारित करने के लिए लागू होते हैं कि Where एक्सटेंशन विधि कहलाएगी।

5

आप सही है कि क्वेरी आप ने लिखा के रूप में स्पष्ट रूप से LINQ अपने डेटा के बारे में कुछ भी कल्पना नहीं कर सकते पूरी सूची की गणना करेंगे कर रहे हैं।

आप एक SortedList है, तो आप उस SkipWhile/TakeWhile LINQ तरीकों का उपयोग कर दोहन कर सकते हैं:

var x = x.SkipWhile(kv => kv.Key <= 18).TakeWhile(kv => kv.Key < 21) 

संपादित

@ Davy8 पाठ्यक्रम सबसे खराब स्थिति यह अभी भी एक ही प्रदर्शन किया है की सही है। पहले मूल्य को और अधिक तेज़ी से ढूंढने के तरीके के लिए अन्य उत्तरों देखें।

आप विभिन्न आयु श्रेणियों के लिए एक बार से इस कार्रवाई को और अधिक करने की जरूरत है तो आप शायद यह भी उम्र में समूहीकरण द्वारा गति कर सकते हैं:

var byAge = people.GroupBy(p => p.Age); 

var x = from grp in byAge 
     where grp.Key > 18 && grp.Key < 21 
     from person in grp 
     select person; 
+1

बस 'जहां' की तुलना में निश्चित रूप से बेहतर औसत मामला, लेकिन सबसे बुरे मामले में जहां आप जो मूल्य ले रहे हैं वह अंत में है, यह अभी भी 'कहां' जैसा ही प्रदर्शन होगा। – Davy8

0

एक पूर्व हल कर कंटेनर में, दक्षता द्वारा हासिल की है पहले तत्व को जल्दी से ढूंढना। एक बार जब आप पहला तत्व पाते हैं, तब तक केवल निम्न तत्वों को पुनः प्राप्त करें जब तक कि आप अपनी सीमा का अंत नहीं पाते।

मानते हुए अपने SortedListPerson.Age के अनुसार क्रमबद्ध है, तो आप SortedList.IndexOfKey का उपयोग कर सीमा है, जो एक binary search एल्गोरिथ्म है के पहले तत्व प्राप्त कर सकते हैं; इसलिए, यह विधि एक ओ (लॉग एन) ऑपरेशन है।

(मुझे नहीं लगता कि इतना Enumerable.Where अचानक अधिक बुद्धिमान हो जाता है और द्विआधारी खोज का उपयोग करके रेंज शुरू पाता है आप अपने कोड को बदल सकते हैं है।)

--- संपादित करें ---

असल में, आपको वास्तव में क्या चाहिए List.BinarySearch या Array.BinarySearchSortedList.IndexOfKey सटीक मिलान मौजूद नहीं होने पर आपको निकटतम मैच की अनुक्रमणिका प्राप्त करने नहीं देगा। या आप बस बाइनरी खोज को लागू कर सकते हैं।

संबंधित मुद्दे