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,2012 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 const int DefaultCapacity = 4;
57 _items = EmptyArray<T>.Value;
60 public List (IEnumerable <T> collection)
62 if (collection == null)
63 throw new ArgumentNullException ("collection");
65 // initialize to needed size (if determinable)
66 ICollection <T> c = collection as ICollection <T>;
68 _items = EmptyArray<T>.Value;;
69 AddEnumerable (collection);
72 _items = new T [_size];
77 public List (int capacity)
80 throw new ArgumentOutOfRangeException ("capacity");
81 _items = new T [capacity];
84 internal List (T [] data, int size)
90 public void Add (T item)
92 // If we check to see if we need to grow before trying to grow
93 // we can speed things up by 25%
94 if (_size == _items.Length)
96 _items [_size++] = item;
100 void GrowIfNeeded (int newCount)
102 int minimumSize = _size + newCount;
103 if (minimumSize > _items.Length)
104 Capacity = Math.Max (Math.Max (Capacity * 2, DefaultCapacity), minimumSize);
107 void CheckRange (int idx, int count)
110 throw new ArgumentOutOfRangeException ("index");
113 throw new ArgumentOutOfRangeException ("count");
115 if ((uint) idx + (uint) count > (uint) _size)
116 throw new ArgumentException ("index and count exceed length of list");
119 void CheckRangeOutOfRange (int idx, int count)
122 throw new ArgumentOutOfRangeException ("index");
125 throw new ArgumentOutOfRangeException ("count");
127 if ((uint) idx + (uint) count > (uint) _size)
128 throw new ArgumentOutOfRangeException ("index and count exceed length of list");
131 void AddCollection (ICollection <T> collection)
133 int collectionCount = collection.Count;
134 if (collectionCount == 0)
137 GrowIfNeeded (collectionCount);
138 collection.CopyTo (_items, _size);
139 _size += collectionCount;
142 void AddEnumerable (IEnumerable <T> enumerable)
144 foreach (T t in enumerable)
150 public void AddRange (IEnumerable <T> collection)
152 if (collection == null)
153 throw new ArgumentNullException ("collection");
155 ICollection <T> c = collection as ICollection <T>;
159 AddEnumerable (collection);
163 public ReadOnlyCollection <T> AsReadOnly ()
165 return new ReadOnlyCollection <T> (this);
168 public int BinarySearch (T item)
170 return Array.BinarySearch <T> (_items, 0, _size, item);
173 public int BinarySearch (T item, IComparer <T> comparer)
175 return Array.BinarySearch <T> (_items, 0, _size, item, comparer);
178 public int BinarySearch (int index, int count, T item, IComparer <T> comparer)
180 CheckRange (index, count);
181 return Array.BinarySearch <T> (_items, index, count, item, comparer);
186 Array.Clear (_items, 0, _items.Length);
191 public bool Contains (T item)
193 return Array.IndexOf<T>(_items, item, 0, _size) != -1;
196 public List <TOutput> ConvertAll <TOutput> (Converter <T, TOutput> converter)
198 if (converter == null)
199 throw new ArgumentNullException ("converter");
200 List <TOutput> u = new List <TOutput> (_size);
201 for (int i = 0; i < _size; i++)
202 u._items[i] = converter(_items[i]);
208 public void CopyTo (T [] array)
210 Array.Copy (_items, 0, array, 0, _size);
213 public void CopyTo (T [] array, int arrayIndex)
215 Array.Copy (_items, 0, array, arrayIndex, _size);
218 public void CopyTo (int index, T [] array, int arrayIndex, int count)
220 CheckRange (index, count);
221 Array.Copy (_items, index, array, arrayIndex, count);
224 public bool Exists (Predicate <T> match)
228 for (int i = 0; i < _size; i++) {
229 var item = _items [i];
237 public T Find (Predicate <T> match)
241 for (int i = 0; i < _size; i++) {
242 var item = _items [i];
250 static void CheckMatch (Predicate <T> match)
253 throw new ArgumentNullException ("match");
256 public List <T> FindAll (Predicate <T> match)
259 if (this._size <= 0x10000) // <= 8 * 1024 * 8 (8k in stack)
260 return this.FindAllStackBits (match);
262 return this.FindAllList (match);
265 private List <T> FindAllStackBits (Predicate <T> match)
269 uint *bits = stackalloc uint [(this._size / 32) + 1];
272 uint bitmask = 0x80000000;
274 for (int i = 0; i < this._size; i++)
276 if (match (this._items [i]))
278 (*ptr) = (*ptr) | bitmask;
282 bitmask = bitmask >> 1;
286 bitmask = 0x80000000;
290 T [] results = new T [found];
291 bitmask = 0x80000000;
294 for (int i = 0; i < this._size && j < found; i++)
296 if (((*ptr) & bitmask) == bitmask)
297 results [j++] = this._items [i];
299 bitmask = bitmask >> 1;
303 bitmask = 0x80000000;
307 return new List <T> (results, found);
311 private List <T> FindAllList (Predicate <T> match)
313 List <T> results = new List <T> ();
314 for (int i = 0; i < this._size; i++)
315 if (match (this._items [i]))
316 results.Add (this._items [i]);
321 public int FindIndex (Predicate <T> match)
324 return Array.GetIndex (_items, 0, _size, match);
327 public int FindIndex (int startIndex, Predicate <T> match)
330 CheckStartIndex (startIndex);
331 return Array.GetIndex (_items, startIndex, _size - startIndex, match);
333 public int FindIndex (int startIndex, int count, Predicate <T> match)
336 CheckRangeOutOfRange (startIndex, count);
337 return Array.GetIndex (_items, startIndex, count, match);
340 public T FindLast (Predicate <T> match)
343 int i = Array.GetLastIndex (_items, 0, _size, match);
344 return i == -1 ? default (T) : this [i];
347 public int FindLastIndex (Predicate <T> match)
350 return Array.GetLastIndex (_items, 0, _size, match);
353 public int FindLastIndex (int startIndex, Predicate <T> match)
356 CheckStartIndex (startIndex);
357 return Array.GetLastIndex (_items, 0, startIndex + 1, match);
360 public int FindLastIndex (int startIndex, int count, Predicate <T> match)
363 CheckStartIndex (startIndex);
366 throw new ArgumentOutOfRangeException ("count");
368 if (startIndex - count + 1 < 0)
369 throw new ArgumentOutOfRangeException ("count must refer to a location within the collection");
371 return Array.GetLastIndex (_items, startIndex - count + 1, count, match);
374 public void ForEach (Action<T> action)
377 throw new ArgumentNullException ("action");
379 var version =_version;
380 for (int i = 0; i < _size; i++) {
381 if (_version != version)
382 throw Enumerator.GetModifiedCollectionException ();
388 public Enumerator GetEnumerator ()
390 return new Enumerator (this);
393 public List <T> GetRange (int index, int count)
395 CheckRange (index, count);
396 T [] tmpArray = new T [count];
397 Array.Copy (_items, index, tmpArray, 0, count);
398 return new List <T> (tmpArray, count);
401 public int IndexOf (T item)
403 return Array.IndexOf<T> (_items, item, 0, _size);
406 public int IndexOf (T item, int index)
409 return Array.IndexOf<T> (_items, item, index, _size - index);
412 public int IndexOf (T item, int index, int count)
415 throw new ArgumentOutOfRangeException ("index");
418 throw new ArgumentOutOfRangeException ("count");
420 if ((uint) index + (uint) count > (uint) _size)
421 throw new ArgumentOutOfRangeException ("index and count exceed length of list");
423 return Array.IndexOf<T> (_items, item, index, count);
426 void Shift (int start, int delta)
432 Array.Copy (_items, start, _items, start + delta, _size - start);
437 Array.Clear (_items, _size, -delta);
440 void CheckIndex (int index)
442 if (index < 0 || (uint) index > (uint) _size)
443 throw new ArgumentOutOfRangeException ("index");
446 void CheckStartIndex (int index)
448 if (index < 0 || (uint) index > (uint) _size)
449 throw new ArgumentOutOfRangeException ("startIndex");
452 public void Insert (int index, T item)
455 if (_size == _items.Length)
458 _items[index] = item;
462 public void InsertRange (int index, IEnumerable <T> collection)
464 if (collection == null)
465 throw new ArgumentNullException ("collection");
468 if (collection == this) {
469 T[] buffer = new T[_size];
471 GrowIfNeeded (_size);
472 Shift (index, buffer.Length);
473 Array.Copy (buffer, 0, _items, index, buffer.Length);
475 ICollection <T> c = collection as ICollection <T>;
477 InsertCollection (index, c);
479 InsertEnumeration (index, collection);
484 void InsertCollection (int index, ICollection <T> collection)
486 int collectionCount = collection.Count;
487 GrowIfNeeded (collectionCount);
489 Shift (index, collectionCount);
490 collection.CopyTo (_items, index);
493 void InsertEnumeration (int index, IEnumerable <T> enumerable)
495 foreach (T t in enumerable)
499 public int LastIndexOf (T item)
503 return Array.LastIndexOf<T> (_items, item, _size - 1, _size);
506 public int LastIndexOf (T item, int index)
509 return Array.LastIndexOf<T> (_items, item, index, index + 1);
512 public int LastIndexOf (T item, int index, int count)
515 throw new ArgumentOutOfRangeException ("index", index, "index is negative");
518 throw new ArgumentOutOfRangeException ("count", count, "count is negative");
520 if (index - count + 1 < 0)
521 throw new ArgumentOutOfRangeException ("cound", count, "count is too large");
523 return Array.LastIndexOf<T> (_items, item, index, count);
526 public bool Remove (T item)
528 int loc = IndexOf (item);
535 public int RemoveAll (Predicate <T> match)
541 // Find the first item to remove
542 for (i = 0; i < _size; i++)
543 if (match(_items[i]))
551 // Remove any additional items
552 for (j = i + 1; j < _size; j++)
554 if (!match(_items[j]))
555 _items[i++] = _items[j];
558 Array.Clear (_items, i, j - i);
564 public void RemoveAt (int index)
566 if (index < 0 || (uint)index >= (uint)_size)
567 throw new ArgumentOutOfRangeException("index");
569 Array.Clear (_items, _size, 1);
573 public void RemoveRange (int index, int count)
575 CheckRange (index, count);
577 Shift (index, -count);
578 Array.Clear (_items, _size, count);
583 public void Reverse ()
585 Array.Reverse (_items, 0, _size);
588 public void Reverse (int index, int count)
590 CheckRange (index, count);
591 Array.Reverse (_items, index, count);
597 Array.Sort<T> (_items, 0, _size);
600 public void Sort (IComparer <T> comparer)
602 Array.Sort<T> (_items, 0, _size, comparer);
606 public void Sort (Comparison <T> comparison)
608 if (comparison == null)
609 throw new ArgumentNullException ("comparison");
611 Array.SortImpl<T> (_items, _size, comparison);
615 public void Sort (int index, int count, IComparer <T> comparer)
617 CheckRange (index, count);
618 Array.Sort<T> (_items, index, count, comparer);
622 public T [] ToArray ()
624 T [] t = new T [_size];
625 Array.Copy (_items, t, _size);
630 public void TrimExcess ()
635 public bool TrueForAll (Predicate <T> match)
639 for (int i = 0; i < _size; i++)
640 if (!match(_items[i]))
646 public int Capacity {
648 return _items.Length;
651 if ((uint) value < (uint) _size)
652 throw new ArgumentOutOfRangeException ();
654 Array.Resize (ref _items, value);
659 get { return _size; }
662 public T this [int index] {
663 [MethodImpl ((MethodImplOptions)256)]
665 if ((uint) index >= (uint) _size)
666 throw new ArgumentOutOfRangeException ("index");
667 return Array.UnsafeLoad (_items, index);
670 [MethodImpl ((MethodImplOptions)256)]
672 if ((uint) index >= (uint) _size)
673 throw new ArgumentOutOfRangeException ("index");
674 _items [index] = value;
679 #region Interface implementations.
680 IEnumerator <T> IEnumerable <T>.GetEnumerator ()
682 return GetEnumerator ();
685 void ICollection.CopyTo (Array array, int arrayIndex)
688 throw new ArgumentNullException ("array");
689 if (array.Rank > 1 || array.GetLowerBound (0) != 0)
690 throw new ArgumentException ("Array must be zero based and single dimentional", "array");
691 Array.Copy (_items, 0, array, arrayIndex, _size);
694 IEnumerator IEnumerable.GetEnumerator ()
696 return GetEnumerator ();
699 int IList.Add (object item)
704 } catch (NullReferenceException) {
705 } catch (InvalidCastException) {
707 throw new ArgumentException ("item");
710 bool IList.Contains (object item)
713 return Contains ((T) item);
714 } catch (NullReferenceException) {
715 } catch (InvalidCastException) {
720 int IList.IndexOf (object item)
723 return IndexOf ((T) item);
724 } catch (NullReferenceException) {
725 } catch (InvalidCastException) {
730 void IList.Insert (int index, object item)
732 // We need to check this first because, even if the
733 // item is null or not the correct type, we need to
734 // return an ArgumentOutOfRange exception if the
735 // index is out of range
738 Insert (index, (T) item);
740 } catch (NullReferenceException) {
741 } catch (InvalidCastException) {
743 throw new ArgumentException ("item");
746 void IList.Remove (object item)
751 } catch (NullReferenceException) {
752 } catch (InvalidCastException) {
754 // Swallow the exception--if we can't cast to the
755 // correct type then we've already "succeeded" in
756 // removing the item from the List.
759 bool ICollection <T>.IsReadOnly {
760 get { return false; }
762 bool ICollection.IsSynchronized {
763 get { return false; }
766 object ICollection.SyncRoot {
769 bool IList.IsFixedSize {
770 get { return false; }
773 bool IList.IsReadOnly {
774 get { return false; }
777 object IList.this [int index] {
778 get { return this [index]; }
781 this [index] = (T) value;
783 } catch (NullReferenceException) {
784 // can happen when 'value' is null and T is a valuetype
785 } catch (InvalidCastException) {
787 throw new ArgumentException ("value");
793 public struct Enumerator : IEnumerator <T>, IDisposable {
800 internal Enumerator (List <T> l)
807 public void Dispose ()
811 public bool MoveNext ()
815 if ((uint)next < (uint)list._size && ver == list._version) {
816 current = list._items [next++];
820 if (ver != l._version)
821 throw GetModifiedCollectionException ();
828 get { return current; }
831 void IEnumerator.Reset ()
833 if (ver != l._version)
834 throw GetModifiedCollectionException ();
837 current = default (T);
840 object IEnumerator.Current {
842 if (ver != l._version)
843 throw GetModifiedCollectionException ();
846 throw new InvalidOperationException ();
851 internal static InvalidOperationException GetModifiedCollectionException ()
853 return new InvalidOperationException ("Collection was modified; enumeration operation may not execute.");