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)
9 // Marek Safar (marek.safar@gmail.com)
11 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2005 David Waite
13 // Copyright (C) 2011 Xamarin, Inc (http://www.xamarin.com)
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System.Collections.ObjectModel;
36 using System.Runtime.InteropServices;
37 using System.Diagnostics;
38 using System.Runtime.CompilerServices;
40 namespace System.Collections.Generic {
42 [DebuggerDisplay ("Count={Count}")]
43 [DebuggerTypeProxy (typeof (CollectionDebuggerView<>))]
44 public class List<T> : IList<T>, IList
53 static readonly T [] EmptyArray = new T [0];
54 const int DefaultCapacity = 4;
61 public List (IEnumerable <T> collection)
63 if (collection == null)
64 throw new ArgumentNullException ("collection");
66 // initialize to needed size (if determinable)
67 ICollection <T> c = collection as ICollection <T>;
70 AddEnumerable (collection);
73 _items = new T [Math.Max (_size, DefaultCapacity)];
78 public List (int capacity)
81 throw new ArgumentOutOfRangeException ("capacity");
82 _items = new T [capacity];
85 internal List (T [] data, int size)
91 public void Add (T item)
93 // If we check to see if we need to grow before trying to grow
94 // we can speed things up by 25%
95 if (_size == _items.Length)
97 _items [_size ++] = item;
101 void GrowIfNeeded (int newCount)
103 int minimumSize = _size + newCount;
104 if (minimumSize > _items.Length)
105 Capacity = Math.Max (Math.Max (Capacity * 2, DefaultCapacity), minimumSize);
108 void CheckRange (int idx, int count)
111 throw new ArgumentOutOfRangeException ("index");
114 throw new ArgumentOutOfRangeException ("count");
116 if ((uint) idx + (uint) count > (uint) _size)
117 throw new ArgumentException ("index and count exceed length of list");
120 void AddCollection (ICollection <T> collection)
122 int collectionCount = collection.Count;
123 if (collectionCount == 0)
126 GrowIfNeeded (collectionCount);
127 collection.CopyTo (_items, _size);
128 _size += collectionCount;
131 void AddEnumerable (IEnumerable <T> enumerable)
133 foreach (T t in enumerable)
139 public void AddRange (IEnumerable <T> collection)
141 if (collection == null)
142 throw new ArgumentNullException ("collection");
144 ICollection <T> c = collection as ICollection <T>;
148 AddEnumerable (collection);
152 public ReadOnlyCollection <T> AsReadOnly ()
154 return new ReadOnlyCollection <T> (this);
157 public int BinarySearch (T item)
159 return Array.BinarySearch <T> (_items, 0, _size, item);
162 public int BinarySearch (T item, IComparer <T> comparer)
164 return Array.BinarySearch <T> (_items, 0, _size, item, comparer);
167 public int BinarySearch (int index, int count, T item, IComparer <T> comparer)
169 CheckRange (index, count);
170 return Array.BinarySearch <T> (_items, index, count, item, comparer);
175 Array.Clear (_items, 0, _items.Length);
180 public bool Contains (T item)
182 return Array.IndexOf<T>(_items, item, 0, _size) != -1;
185 public List <TOutput> ConvertAll <TOutput> (Converter <T, TOutput> converter)
187 if (converter == null)
188 throw new ArgumentNullException ("converter");
189 List <TOutput> u = new List <TOutput> (_size);
190 for (int i = 0; i < _size; i++)
191 u._items[i] = converter(_items[i]);
197 public void CopyTo (T [] array)
199 Array.Copy (_items, 0, array, 0, _size);
202 public void CopyTo (T [] array, int arrayIndex)
204 Array.Copy (_items, 0, array, arrayIndex, _size);
207 public void CopyTo (int index, T [] array, int arrayIndex, int count)
209 CheckRange (index, count);
210 Array.Copy (_items, index, array, arrayIndex, count);
213 public bool Exists (Predicate <T> match)
216 return GetIndex(0, _size, match) != -1;
219 public T Find (Predicate <T> match)
222 int i = GetIndex(0, _size, match);
223 return (i != -1) ? _items [i] : default (T);
226 static void CheckMatch (Predicate <T> match)
229 throw new ArgumentNullException ("match");
232 public List <T> FindAll (Predicate <T> match)
235 if (this._size <= 0x10000) // <= 8 * 1024 * 8 (8k in stack)
236 return this.FindAllStackBits (match);
238 return this.FindAllList (match);
241 private List <T> FindAllStackBits (Predicate <T> match)
245 uint *bits = stackalloc uint [(this._size / 32) + 1];
248 uint bitmask = 0x80000000;
250 for (int i = 0; i < this._size; i++)
252 if (match (this._items [i]))
254 (*ptr) = (*ptr) | bitmask;
258 bitmask = bitmask >> 1;
262 bitmask = 0x80000000;
266 T [] results = new T [found];
267 bitmask = 0x80000000;
270 for (int i = 0; i < this._size && j < found; i++)
272 if (((*ptr) & bitmask) == bitmask)
273 results [j++] = this._items [i];
275 bitmask = bitmask >> 1;
279 bitmask = 0x80000000;
283 return new List <T> (results, found);
287 private List <T> FindAllList (Predicate <T> match)
289 List <T> results = new List <T> ();
290 for (int i = 0; i < this._size; i++)
291 if (match (this._items [i]))
292 results.Add (this._items [i]);
297 public int FindIndex (Predicate <T> match)
300 return GetIndex (0, _size, match);
303 public int FindIndex (int startIndex, Predicate <T> match)
306 CheckIndex (startIndex);
307 return GetIndex (startIndex, _size - startIndex, match);
309 public int FindIndex (int startIndex, int count, Predicate <T> match)
312 CheckRange (startIndex, count);
313 return GetIndex (startIndex, count, match);
315 int GetIndex (int startIndex, int count, Predicate <T> match)
317 int end = startIndex + count;
318 for (int i = startIndex; i < end; i ++)
319 if (match (_items [i]))
325 public T FindLast (Predicate <T> match)
328 int i = GetLastIndex (0, _size, match);
329 return i == -1 ? default (T) : this [i];
332 public int FindLastIndex (Predicate <T> match)
335 return GetLastIndex (0, _size, match);
338 public int FindLastIndex (int startIndex, Predicate <T> match)
341 CheckIndex (startIndex);
342 return GetLastIndex (0, startIndex + 1, match);
345 public int FindLastIndex (int startIndex, int count, Predicate <T> match)
348 int start = startIndex - count + 1;
349 CheckRange (start, count);
350 return GetLastIndex (start, count, match);
353 int GetLastIndex (int startIndex, int count, Predicate <T> match)
355 // unlike FindLastIndex, takes regular params for search range
356 for (int i = startIndex + count; i != startIndex;)
357 if (match (_items [--i]))
362 public void ForEach (Action <T> action)
365 throw new ArgumentNullException ("action");
366 for(int i=0; i < _size; i++)
370 public Enumerator GetEnumerator ()
372 return new Enumerator (this);
375 public List <T> GetRange (int index, int count)
377 CheckRange (index, count);
378 T [] tmpArray = new T [count];
379 Array.Copy (_items, index, tmpArray, 0, count);
380 return new List <T> (tmpArray, count);
383 public int IndexOf (T item)
385 return Array.IndexOf<T> (_items, item, 0, _size);
388 public int IndexOf (T item, int index)
391 return Array.IndexOf<T> (_items, item, index, _size - index);
394 public int IndexOf (T item, int index, int count)
397 throw new ArgumentOutOfRangeException ("index");
400 throw new ArgumentOutOfRangeException ("count");
402 if ((uint) index + (uint) count > (uint) _size)
403 throw new ArgumentOutOfRangeException ("index and count exceed length of list");
405 return Array.IndexOf<T> (_items, item, index, count);
408 void Shift (int start, int delta)
414 Array.Copy (_items, start, _items, start + delta, _size - start);
419 Array.Clear (_items, _size, -delta);
422 void CheckIndex (int index)
424 if (index < 0 || (uint) index > (uint) _size)
425 throw new ArgumentOutOfRangeException ("index");
428 public void Insert (int index, T item)
431 if (_size == _items.Length)
434 _items[index] = item;
438 public void InsertRange (int index, IEnumerable <T> collection)
440 if (collection == null)
441 throw new ArgumentNullException ("collection");
444 if (collection == this) {
445 T[] buffer = new T[_size];
447 GrowIfNeeded (_size);
448 Shift (index, buffer.Length);
449 Array.Copy (buffer, 0, _items, index, buffer.Length);
451 ICollection <T> c = collection as ICollection <T>;
453 InsertCollection (index, c);
455 InsertEnumeration (index, collection);
460 void InsertCollection (int index, ICollection <T> collection)
462 int collectionCount = collection.Count;
463 GrowIfNeeded (collectionCount);
465 Shift (index, collectionCount);
466 collection.CopyTo (_items, index);
469 void InsertEnumeration (int index, IEnumerable <T> enumerable)
471 foreach (T t in enumerable)
475 public int LastIndexOf (T item)
479 return Array.LastIndexOf<T> (_items, item, _size - 1, _size);
482 public int LastIndexOf (T item, int index)
485 return Array.LastIndexOf<T> (_items, item, index, index + 1);
488 public int LastIndexOf (T item, int index, int count)
491 throw new ArgumentOutOfRangeException ("index", index, "index is negative");
494 throw new ArgumentOutOfRangeException ("count", count, "count is negative");
496 if (index - count + 1 < 0)
497 throw new ArgumentOutOfRangeException ("cound", count, "count is too large");
499 return Array.LastIndexOf<T> (_items, item, index, count);
502 public bool Remove (T item)
504 int loc = IndexOf (item);
511 public int RemoveAll (Predicate <T> match)
517 // Find the first item to remove
518 for (i = 0; i < _size; i++)
519 if (match(_items[i]))
527 // Remove any additional items
528 for (j = i + 1; j < _size; j++)
530 if (!match(_items[j]))
531 _items[i++] = _items[j];
534 Array.Clear (_items, i, j - i);
540 public void RemoveAt (int index)
542 if (index < 0 || (uint)index >= (uint)_size)
543 throw new ArgumentOutOfRangeException("index");
545 Array.Clear (_items, _size, 1);
549 public void RemoveRange (int index, int count)
551 CheckRange (index, count);
553 Shift (index, -count);
554 Array.Clear (_items, _size, count);
559 public void Reverse ()
561 Array.Reverse (_items, 0, _size);
564 public void Reverse (int index, int count)
566 CheckRange (index, count);
567 Array.Reverse (_items, index, count);
573 Array.Sort<T> (_items, 0, _size);
576 public void Sort (IComparer <T> comparer)
578 Array.Sort<T> (_items, 0, _size, comparer);
582 public void Sort (Comparison <T> comparison)
584 if (comparison == null)
585 throw new ArgumentNullException ("comparison");
587 Array.SortImpl<T> (_items, _size, comparison);
591 public void Sort (int index, int count, IComparer <T> comparer)
593 CheckRange (index, count);
594 Array.Sort<T> (_items, index, count, comparer);
598 public T [] ToArray ()
600 T [] t = new T [_size];
601 Array.Copy (_items, t, _size);
606 public void TrimExcess ()
611 public bool TrueForAll (Predicate <T> match)
615 for (int i = 0; i < _size; i++)
616 if (!match(_items[i]))
622 public int Capacity {
624 return _items.Length;
627 if ((uint) value < (uint) _size)
628 throw new ArgumentOutOfRangeException ();
630 Array.Resize (ref _items, value);
635 get { return _size; }
638 public T this [int index] {
639 [MethodImpl ((MethodImplOptions)256)]
641 if ((uint) index >= (uint) _size)
642 throw new ArgumentOutOfRangeException ("index");
643 return Array.UnsafeLoad (_items, index);
646 [MethodImpl ((MethodImplOptions)256)]
648 if ((uint) index >= (uint) _size)
649 throw new ArgumentOutOfRangeException ("index");
650 Array.UnsafeStore (_items, index, value);
655 #region Interface implementations.
656 IEnumerator <T> IEnumerable <T>.GetEnumerator ()
658 return GetEnumerator ();
661 void ICollection.CopyTo (Array array, int arrayIndex)
664 throw new ArgumentNullException ("array");
665 if (array.Rank > 1 || array.GetLowerBound (0) != 0)
666 throw new ArgumentException ("Array must be zero based and single dimentional", "array");
667 Array.Copy (_items, 0, array, arrayIndex, _size);
670 IEnumerator IEnumerable.GetEnumerator ()
672 return GetEnumerator ();
675 int IList.Add (object item)
680 } catch (NullReferenceException) {
681 } catch (InvalidCastException) {
683 throw new ArgumentException ("item");
686 bool IList.Contains (object item)
689 return Contains ((T) item);
690 } catch (NullReferenceException) {
691 } catch (InvalidCastException) {
696 int IList.IndexOf (object item)
699 return IndexOf ((T) item);
700 } catch (NullReferenceException) {
701 } catch (InvalidCastException) {
706 void IList.Insert (int index, object item)
708 // We need to check this first because, even if the
709 // item is null or not the correct type, we need to
710 // return an ArgumentOutOfRange exception if the
711 // index is out of range
714 Insert (index, (T) item);
716 } catch (NullReferenceException) {
717 } catch (InvalidCastException) {
719 throw new ArgumentException ("item");
722 void IList.Remove (object item)
727 } catch (NullReferenceException) {
728 } catch (InvalidCastException) {
730 // Swallow the exception--if we can't cast to the
731 // correct type then we've already "succeeded" in
732 // removing the item from the List.
735 bool ICollection <T>.IsReadOnly {
736 get { return false; }
738 bool ICollection.IsSynchronized {
739 get { return false; }
742 object ICollection.SyncRoot {
745 bool IList.IsFixedSize {
746 get { return false; }
749 bool IList.IsReadOnly {
750 get { return false; }
753 object IList.this [int index] {
754 get { return this [index]; }
757 this [index] = (T) value;
759 } catch (NullReferenceException) {
760 // can happen when 'value' is null and T is a valuetype
761 } catch (InvalidCastException) {
763 throw new ArgumentException ("value");
769 public struct Enumerator : IEnumerator <T>, IDisposable {
776 internal Enumerator (List <T> l)
783 public void Dispose ()
789 if (ver != l._version)
790 throw new InvalidOperationException (
791 "Collection was modified; enumeration operation may not execute.");
794 public bool MoveNext ()
801 if (next < l._size) {
802 current = l._items [next++];
811 get { return current; }
814 void IEnumerator.Reset ()
820 object IEnumerator.Current {
824 throw new InvalidOperationException ();