2 // System.Collections.Generic.List
5 // Ben Maurer (bmaurer@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Carlos Alberto Cortez (calberto.cortez@gmail.com)
8 // David Waite (mass@akuma.org)
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 // Copyright (C) 2005 David Waite
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Collections.ObjectModel;
34 using System.Runtime.InteropServices;
35 using System.Diagnostics;
37 namespace System.Collections.Generic {
39 [DebuggerDisplay ("Count={Count}")]
40 [DebuggerTypeProxy (typeof (CollectionDebuggerView<>))]
41 public class List <T> : IList <T>, IList, ICollection {
46 static readonly T [] EmptyArray = new T [0];
47 const int DefaultCapacity = 4;
54 public List (IEnumerable <T> collection)
56 if (collection == null)
57 throw new ArgumentNullException ("collection");
59 // initialize to needed size (if determinable)
60 ICollection <T> c = collection as ICollection <T>;
63 AddEnumerable (collection);
66 _items = new T [Math.Max (_size, DefaultCapacity)];
71 public List (int capacity)
74 throw new ArgumentOutOfRangeException ("capacity");
75 _items = new T [capacity];
78 internal List (T [] data, int size)
84 public void Add (T item)
86 // If we check to see if we need to grow before trying to grow
87 // we can speed things up by 25%
88 if (_size == _items.Length)
90 _items [_size ++] = item;
94 void GrowIfNeeded (int newCount)
96 int minimumSize = _size + newCount;
97 if (minimumSize > _items.Length)
98 Capacity = Math.Max (Math.Max (Capacity * 2, DefaultCapacity), minimumSize);
101 void CheckRange (int idx, int count)
104 throw new ArgumentOutOfRangeException ("index");
107 throw new ArgumentOutOfRangeException ("count");
109 if ((uint) idx + (uint) count > (uint) _size)
110 throw new ArgumentException ("index and count exceed length of list");
113 void AddCollection (ICollection <T> collection)
115 int collectionCount = collection.Count;
116 if (collectionCount == 0)
119 GrowIfNeeded (collectionCount);
120 collection.CopyTo (_items, _size);
121 _size += collectionCount;
124 void AddEnumerable (IEnumerable <T> enumerable)
126 foreach (T t in enumerable)
132 public void AddRange (IEnumerable <T> collection)
134 if (collection == null)
135 throw new ArgumentNullException ("collection");
137 ICollection <T> c = collection as ICollection <T>;
141 AddEnumerable (collection);
145 public ReadOnlyCollection <T> AsReadOnly ()
147 return new ReadOnlyCollection <T> (this);
150 public int BinarySearch (T item)
152 return Array.BinarySearch <T> (_items, 0, _size, item);
155 public int BinarySearch (T item, IComparer <T> comparer)
157 return Array.BinarySearch <T> (_items, 0, _size, item, comparer);
160 public int BinarySearch (int index, int count, T item, IComparer <T> comparer)
162 CheckRange (index, count);
163 return Array.BinarySearch <T> (_items, index, count, item, comparer);
168 Array.Clear (_items, 0, _items.Length);
173 public bool Contains (T item)
175 return Array.IndexOf<T>(_items, item, 0, _size) != -1;
178 public List <TOutput> ConvertAll <TOutput> (Converter <T, TOutput> converter)
180 if (converter == null)
181 throw new ArgumentNullException ("converter");
182 List <TOutput> u = new List <TOutput> (_size);
183 for (int i = 0; i < _size; i++)
184 u._items[i] = converter(_items[i]);
190 public void CopyTo (T [] array)
192 Array.Copy (_items, 0, array, 0, _size);
195 public void CopyTo (T [] array, int arrayIndex)
197 Array.Copy (_items, 0, array, arrayIndex, _size);
200 public void CopyTo (int index, T [] array, int arrayIndex, int count)
202 CheckRange (index, count);
203 Array.Copy (_items, index, array, arrayIndex, count);
206 public bool Exists (Predicate <T> match)
209 return GetIndex(0, _size, match) != -1;
212 public T Find (Predicate <T> match)
215 int i = GetIndex(0, _size, match);
216 return (i != -1) ? _items [i] : default (T);
219 static void CheckMatch (Predicate <T> match)
222 throw new ArgumentNullException ("match");
225 public List <T> FindAll (Predicate <T> match)
228 if (this._size <= 0x10000) // <= 8 * 1024 * 8 (8k in stack)
229 return this.FindAllStackBits (match);
231 return this.FindAllList (match);
234 private List <T> FindAllStackBits (Predicate <T> match)
238 uint *bits = stackalloc uint [(this._size / 32) + 1];
241 uint bitmask = 0x80000000;
243 for (int i = 0; i < this._size; i++)
245 if (match (this._items [i]))
247 (*ptr) = (*ptr) | bitmask;
251 bitmask = bitmask >> 1;
255 bitmask = 0x80000000;
259 T [] results = new T [found];
260 bitmask = 0x80000000;
263 for (int i = 0; i < this._size && j < found; i++)
265 if (((*ptr) & bitmask) == bitmask)
266 results [j++] = this._items [i];
268 bitmask = bitmask >> 1;
272 bitmask = 0x80000000;
276 return new List <T> (results, found);
280 private List <T> FindAllList (Predicate <T> match)
282 List <T> results = new List <T> ();
283 for (int i = 0; i < this._size; i++)
284 if (match (this._items [i]))
285 results.Add (this._items [i]);
290 public int FindIndex (Predicate <T> match)
293 return GetIndex (0, _size, match);
296 public int FindIndex (int startIndex, Predicate <T> match)
299 CheckIndex (startIndex);
300 return GetIndex (startIndex, _size - startIndex, match);
302 public int FindIndex (int startIndex, int count, Predicate <T> match)
305 CheckRange (startIndex, count);
306 return GetIndex (startIndex, count, match);
308 int GetIndex (int startIndex, int count, Predicate <T> match)
310 int end = startIndex + count;
311 for (int i = startIndex; i < end; i ++)
312 if (match (_items [i]))
318 public T FindLast (Predicate <T> match)
321 int i = GetLastIndex (0, _size, match);
322 return i == -1 ? default (T) : this [i];
325 public int FindLastIndex (Predicate <T> match)
328 return GetLastIndex (0, _size, match);
331 public int FindLastIndex (int startIndex, Predicate <T> match)
334 CheckIndex (startIndex);
335 return GetLastIndex (0, startIndex + 1, match);
338 public int FindLastIndex (int startIndex, int count, Predicate <T> match)
341 int start = startIndex - count + 1;
342 CheckRange (start, count);
343 return GetLastIndex (start, count, match);
346 int GetLastIndex (int startIndex, int count, Predicate <T> match)
348 // unlike FindLastIndex, takes regular params for search range
349 for (int i = startIndex + count; i != startIndex;)
350 if (match (_items [--i]))
355 public void ForEach (Action <T> action)
358 throw new ArgumentNullException ("action");
359 for(int i=0; i < _size; i++)
363 public Enumerator GetEnumerator ()
365 return new Enumerator (this);
368 public List <T> GetRange (int index, int count)
370 CheckRange (index, count);
371 T [] tmpArray = new T [count];
372 Array.Copy (_items, index, tmpArray, 0, count);
373 return new List <T> (tmpArray, count);
376 public int IndexOf (T item)
378 return Array.IndexOf<T> (_items, item, 0, _size);
381 public int IndexOf (T item, int index)
384 return Array.IndexOf<T> (_items, item, index, _size - index);
387 public int IndexOf (T item, int index, int count)
390 throw new ArgumentOutOfRangeException ("index");
393 throw new ArgumentOutOfRangeException ("count");
395 if ((uint) index + (uint) count > (uint) _size)
396 throw new ArgumentOutOfRangeException ("index and count exceed length of list");
398 return Array.IndexOf<T> (_items, item, index, count);
401 void Shift (int start, int delta)
407 Array.Copy (_items, start, _items, start + delta, _size - start);
412 Array.Clear (_items, _size, -delta);
415 void CheckIndex (int index)
417 if (index < 0 || (uint) index > (uint) _size)
418 throw new ArgumentOutOfRangeException ("index");
421 public void Insert (int index, T item)
424 if (_size == _items.Length)
427 _items[index] = item;
431 public void InsertRange (int index, IEnumerable <T> collection)
433 if (collection == null)
434 throw new ArgumentNullException ("collection");
437 if (collection == this) {
438 T[] buffer = new T[_size];
440 GrowIfNeeded (_size);
441 Shift (index, buffer.Length);
442 Array.Copy (buffer, 0, _items, index, buffer.Length);
444 ICollection <T> c = collection as ICollection <T>;
446 InsertCollection (index, c);
448 InsertEnumeration (index, collection);
453 void InsertCollection (int index, ICollection <T> collection)
455 int collectionCount = collection.Count;
456 GrowIfNeeded (collectionCount);
458 Shift (index, collectionCount);
459 collection.CopyTo (_items, index);
462 void InsertEnumeration (int index, IEnumerable <T> enumerable)
464 foreach (T t in enumerable)
468 public int LastIndexOf (T item)
470 return Array.LastIndexOf<T> (_items, item, _size - 1, _size);
473 public int LastIndexOf (T item, int index)
476 return Array.LastIndexOf<T> (_items, item, index, index + 1);
479 public int LastIndexOf (T item, int index, int count)
482 throw new ArgumentOutOfRangeException ("index", index, "index is negative");
485 throw new ArgumentOutOfRangeException ("count", count, "count is negative");
487 if (index - count + 1 < 0)
488 throw new ArgumentOutOfRangeException ("cound", count, "count is too large");
490 return Array.LastIndexOf<T> (_items, item, index, count);
493 public bool Remove (T item)
495 int loc = IndexOf (item);
502 public int RemoveAll (Predicate <T> match)
508 // Find the first item to remove
509 for (i = 0; i < _size; i++)
510 if (match(_items[i]))
518 // Remove any additional items
519 for (j = i + 1; j < _size; j++)
521 if (!match(_items[j]))
522 _items[i++] = _items[j];
525 Array.Clear (_items, i, j - i);
531 public void RemoveAt (int index)
533 if (index < 0 || (uint)index >= (uint)_size)
534 throw new ArgumentOutOfRangeException("index");
536 Array.Clear (_items, _size, 1);
540 public void RemoveRange (int index, int count)
542 CheckRange (index, count);
544 Shift (index, -count);
545 Array.Clear (_items, _size, count);
550 public void Reverse ()
552 Array.Reverse (_items, 0, _size);
555 public void Reverse (int index, int count)
557 CheckRange (index, count);
558 Array.Reverse (_items, index, count);
564 Array.Sort<T> (_items, 0, _size);
567 public void Sort (IComparer <T> comparer)
569 Array.Sort<T> (_items, 0, _size, comparer);
573 public void Sort (Comparison <T> comparison)
575 if (comparison == null)
576 throw new ArgumentNullException ("comparison");
578 Array.SortImpl<T> (_items, _size, comparison);
582 public void Sort (int index, int count, IComparer <T> comparer)
584 CheckRange (index, count);
585 Array.Sort<T> (_items, index, count, comparer);
589 public T [] ToArray ()
591 T [] t = new T [_size];
592 Array.Copy (_items, t, _size);
597 public void TrimExcess ()
602 public bool TrueForAll (Predicate <T> match)
606 for (int i = 0; i < _size; i++)
607 if (!match(_items[i]))
613 public int Capacity {
615 return _items.Length;
618 if ((uint) value < (uint) _size)
619 throw new ArgumentOutOfRangeException ();
621 Array.Resize (ref _items, value);
626 get { return _size; }
629 public T this [int index] {
631 if ((uint) index >= (uint) _size)
632 throw new ArgumentOutOfRangeException ("index");
633 return _items [index];
637 if ((uint) index == (uint) _size)
638 throw new ArgumentOutOfRangeException ("index");
639 _items [index] = value;
643 #region Interface implementations.
644 IEnumerator <T> IEnumerable <T>.GetEnumerator ()
646 return GetEnumerator ();
649 void ICollection.CopyTo (Array array, int arrayIndex)
652 throw new ArgumentNullException ("array");
653 if (array.Rank > 1 || array.GetLowerBound (0) != 0)
654 throw new ArgumentException ("Array must be zero based and single dimentional", "array");
655 Array.Copy (_items, 0, array, arrayIndex, _size);
658 IEnumerator IEnumerable.GetEnumerator ()
660 return GetEnumerator ();
663 int IList.Add (object item)
668 } catch (NullReferenceException) {
669 } catch (InvalidCastException) {
671 throw new ArgumentException ("item");
674 bool IList.Contains (object item)
677 return Contains ((T) item);
678 } catch (NullReferenceException) {
679 } catch (InvalidCastException) {
684 int IList.IndexOf (object item)
687 return IndexOf ((T) item);
688 } catch (NullReferenceException) {
689 } catch (InvalidCastException) {
694 void IList.Insert (int index, object item)
696 // We need to check this first because, even if the
697 // item is null or not the correct type, we need to
698 // return an ArgumentOutOfRange exception if the
699 // index is out of range
702 Insert (index, (T) item);
704 } catch (NullReferenceException) {
705 } catch (InvalidCastException) {
707 throw new ArgumentException ("item");
710 void IList.Remove (object item)
715 } catch (NullReferenceException) {
716 } catch (InvalidCastException) {
718 // Swallow the exception--if we can't cast to the
719 // correct type then we've already "succeeded" in
720 // removing the item from the List.
723 bool ICollection <T>.IsReadOnly {
724 get { return false; }
726 bool ICollection.IsSynchronized {
727 get { return false; }
730 object ICollection.SyncRoot {
733 bool IList.IsFixedSize {
734 get { return false; }
737 bool IList.IsReadOnly {
738 get { return false; }
741 object IList.this [int index] {
742 get { return this [index]; }
745 this [index] = (T) value;
747 } catch (NullReferenceException) {
748 // can happen when 'value' is null and T is a valuetype
749 } catch (InvalidCastException) {
751 throw new ArgumentException ("value");
757 public struct Enumerator : IEnumerator <T>, IDisposable {
764 internal Enumerator (List <T> l)
771 public void Dispose ()
779 throw new ObjectDisposedException (GetType ().FullName);
780 if (ver != l._version)
781 throw new InvalidOperationException (
782 "Collection was modified; enumeration operation may not execute.");
785 public bool MoveNext ()
792 if (next < l._size) {
793 current = l._items [next++];
802 get { return current; }
805 void IEnumerator.Reset ()
811 object IEnumerator.Current {
815 throw new InvalidOperationException ();