2011-08-05 14 views
11

हाल ही में, मैं कोड का एक बहुत है कि इस तरह दिखता है लिख दिया गया है:क्या सी # सूची <T> से आइटम को हटा रहा है अन्य आइटम के ऑर्डर बनाए रखें?

List<MyObject> myList = new List<MyObject>(); 
... 
for(int i = 0; i < myList.Count; ++i) 
{ 
    if(/*myList[i] meets removal criteria*/) 
    { 
    myList.RemoveAt(i); 
    --i; //Check this index again for the next item 
    //Do other stuff as well 
    } 
} 

और मैं सिर्फ एक छोटे से पागल है कि शायद सूची हटाने पर वस्तु क्रम को बनाए रखने नहीं है बन गया। मैं निश्चित रूप से जानने के लिए पर्याप्त सी # spec नहीं जानता। क्या कोई यह सत्यापित कर सकता है कि मैं या तो इस पैटर्न के साथ परेशानी नहीं मांग रहा हूं?

संपादित करें: शायद मुझे यह स्पष्ट करना चाहिए कि उपर्युक्त एक बहुत ही सरल उदाहरण है और यदि आइटम को हटाया जाना चाहिए तो अधिक चीजें होती हैं इसलिए मुझे नहीं लगता कि List<T>.RemoveAll() बहुत लागू है। हालांकि यह एक अच्छा काम है। मैंने विशेष रूप से उल्लेख करने के लिए उपरोक्त if() ब्लॉक में एक टिप्पणी जोड़ा है।

+4

बस एक तरफ नोट: आपको आगे की बजाय सूची के पीछे लूप करना चाहिए। मान लें कि आप सूची हटाएं [1] (i = 1)। इससे सूची में बदलाव आएगा, जहां सूची में तत्व [2] अब सूची में है [1]। अब जब आप i = 2 पर कूदते हैं, तो आपने उस तत्व को छोड़ दिया है जो अब सूची में बैठता है [1]। –

+1

आपको RemoveAll() विधि –

+0

के साथ इस पैटर्न को बहुत सरल बनाने में सक्षम होना चाहिए, इसके लिए आप linq का उपयोग क्यों नहीं करते? – Andre

उत्तर

15

List<T>हमेशा जोड़ने, डालने और हटाने के दौरान सापेक्ष क्रम बनाए रखेंगे; अगर यह नहीं होता तो यह एक सूची नहीं होगी।

यहाँ RemoveAt() के लिए (ILSpy'ed) कोड है:

public void RemoveAt(int index) 
{ 
    if (index >= this._size) 
    { 
     ThrowHelper.ThrowArgumentOutOfRangeException(); 
    } 
    this._size--; 
    if (index < this._size) 
    { 
     Array.Copy(this._items, index + 1, this._items, index, this._size - index); 
    } 
    this._items[this._size] = default(T); 
    this._version++; 
} 

नोट सरणी index करने के लिए index + 1 से कॉपी; यह वस्तुओं को थोक में स्थानांतरित किया जा रहा है और सरणी को "निचोड़ने" है। लेकिन निश्चित रूप से तत्वों का पुन: क्रम नहीं है।

+0

रिवर्स में सूची के माध्यम से चलाने और 'RemoveAll()' पर विचार करने के बारे में अतिरिक्त (बहुत उपयोगी) वार्तालाप के अलावा, यह वास्तव में पूछे गए प्रश्न का उत्तर देता है। धन्यवाद! – chaosTechnician

3
परावर्तक से

:

public void RemoveAt(int index) 
{ 
    if (index >= this._size) 
    { 
     ThrowHelper.ThrowArgumentOutOfRangeException(); 
    } 
    this._size--; 
    if (index < this._size) 
    { 
     Array.Copy(this._items, index + 1, this._items, index, this._size - index); 
    } 
    this._items[this._size] = default(T); 
    this._version++; 
} 

तो कम से कम एमएस के साथ 'कार्यान्वयन - आइटम' आदेश RemoveAt पर नहीं बदलता है।

2

जब आप RemoveAt पर कॉल करते हैं, तो आपके द्वारा निकाले गए इंडेक्स के बाद सभी तत्वों की प्रतिलिपि बनाई जाएगी और आगे स्थानांतरित की जाएगी।

अवरोही क्रम में स्थिति सूची को क्रमबद्ध करें और उस क्रम में तत्वों को हटाएं।

foreach (var position in positions.OrderByDescending(x=>x)) 
    list.RemoveAt(position); 

positions इंडेक्स की सूची है। list वह है जिसे आप हटाना चाहते हैं (इसमें वास्तविक डेटा है)।

10

आप वास्तव में सही हैं, List<T>.RemoveAt सूची के आइटमों का क्रम नहीं बदलेगा।

आपका स्निपेट तथापि List<T>.RemoveAll इस तरह उपयोग करने के लिए सरल किया जा सकता है: निम्नलिखित टिप्पणी

List<MyObject> myList = new List<MyObject>(); 
... 
myList.RemoveAll(/* Removal predicate */); 

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

myList.Where(/* Removal predicate */).ToList().ForEach(/* Removal code */); 
myList.RemoveAll(/* Removal predicate */); 
+2

अच्छा बिंदु। पहिया को फिर से शुरू करने की कोई ज़रूरत नहीं है :) +1 –

+0

यह सूची को स्थानांतरित करने के खिलाफ भी रक्षा करनी चाहिए क्योंकि प्रत्येक तत्व को हटा दिया गया है (हटाए गए एक के बाद सीधे तत्व को छोड़ना)। मैं सूची के माध्यम से विपरीत क्रम में आगे बढ़ने का सुझाव देना चाहता हूं, लेकिन वह बहुत बेहतर है! –

+0

मैंने अपना मूल प्रश्न यह कहने के लिए अपडेट किया है कि चूंकि मैं आइटम को हटाने से ज्यादा कुछ कर रहा हूं, इसलिए मुझे नहीं लगता कि 'RemoveAll() 'मेरे लिए चाल करेगा। – chaosTechnician

4

क्रम बनाए रखा जाना चाहिए।

for(int i = myList.Count - 1; i >= 0; i--) 
{ 
    if(/*myList[i] meets removal criteria*/) 
    { 
     myList.RemoveAt(i); 
    } 
} 

या आप RemoveAll method इस्तेमाल कर सकते हैं:

myList.RemoveAll(item => [item meets removal criteria]); 
+0

यह बेहतर क्यों है? –

+4

@ लूप के लिए विपरीत रिवर्स का पालन करना आसान है और मापदंडों को पूरा होने के बाद इंडेक्स को कम करने की आवश्यकता नहीं है। सूची के अंत में और पीछे की ओर काम करके, यदि कोई आइटम हटा दिया जाता है तो लूप सटीक रहेगा। 'RemoveAll' के लिए, यह एक अनुमानक वाला एक-लाइनर है और हमें सूची में लूप करने और इंडेक्स प्रबंधित करने की आवश्यकता नहीं है। –

5

हालांकि स्वीकार किए जाते हैं जवाब मूल प्रश्न के लिए एक महान जवाब है, सिकाडा के जवाब के लिए एक विकल्प का सुझाव एक बेहतर दृष्टिकोण रिवर्स में सूची पार करने के लिए है दृष्टिकोण।

सीएलआर 4 (वीएस 2010) के साथ हम अभी तक एक और दृष्टिकोण प्राप्त करते हैं, जिसका प्रति आइटम केवल एक बार अनुमान लगाने का और लाभ है (और हमारे कोड में दो बार भविष्यवाणी लिखने से बचने के लिए सुविधाजनक बनाना)।

मान लीजिए आप एक IEnumerable<string>:

IEnumerable<string> myList = new[] {"apples", "bananas", "pears", "tomatoes"}; 

आप के अनुसार दो सूचियों में विभाजित करने के लिए आइटम कुछ मानदंड पर खरे उतर रहे हैं या नहीं:

var divided = myList.ToLookup(i => i.Length > 6); 

लौटे वस्तु का एक Dictionary की तरह कुछ हद तक है सूचियों। आप जो कि मानदंड पर खरे उतर रखना चाहते मान लीजिए:

myList = divided[true]; 

और आप अन्य वस्तुओं पर संचालित करने के लिए एक परिचित जरूरी पाश का उपयोग कर सकते हैं:

foreach (var item in divided[false]) 
    Console.WriteLine("Removed " + item); 

नोट List<T> उपयोग करने के लिए कोई जरूरत नहीं है कि विशेष रूप से। हमने किसी मौजूदा सूची को कभी भी संशोधित नहीं किया - हम केवल नए बनाते हैं।

+0

+1 - यह साफ है! मुझे नहीं पता था कि यह अस्तित्व में है। –

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