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.
35 using System.Collections.ObjectModel;
36 using System.Runtime.InteropServices;
38 namespace System.Collections.Generic {
40 public class List <T> : IList <T>, IList, ICollection {
45 static readonly T [] EmptyArray = new T [0];
46 const int DefaultCapacity = 4;
53 public List (IEnumerable <T> collection)
55 CheckCollection (collection);
57 // initialize to needed size (if determinable)
58 ICollection <T> c = collection as ICollection <T>;
62 AddEnumerable (collection);
66 _items = new T [c.Count];
71 public List (int capacity)
74 throw new ArgumentOutOfRangeException ("capacity");
75 _items = new T [capacity];
78 internal List (T [] data, int size)
83 public void Add (T item)
86 _items [_size ++] = item;
90 void GrowIfNeeded (int newCount)
92 int minimumSize = _size + newCount;
93 if (minimumSize > _items.Length)
94 Capacity = Math.Max (Math.Max (Capacity * 2, DefaultCapacity), minimumSize);
97 void CheckRange (int idx, int count)
100 throw new ArgumentOutOfRangeException ("index");
103 throw new ArgumentOutOfRangeException ("count");
105 if ((uint) idx + (uint) count > (uint) _size)
106 throw new ArgumentException ("index and count exceed length of list");
109 void AddCollection (ICollection <T> collection)
111 int collectionCount = collection.Count;
112 GrowIfNeeded (collectionCount);
113 collection.CopyTo (_items, _size);
114 _size += collectionCount;
116 void AddEnumerable (IEnumerable <T> enumerable)
118 foreach (T t in enumerable)
124 public void AddRange (IEnumerable <T> collection)
126 CheckCollection (collection);
128 ICollection <T> c = collection as ICollection <T>;
132 AddEnumerable (collection);
136 public ReadOnlyCollection <T> AsReadOnly ()
138 return new ReadOnlyCollection <T> (this);
141 public int BinarySearch (T item)
143 return Array.BinarySearch <T> (_items, 0, _size, item);
146 public int BinarySearch (T item, IComparer <T> comparer)
148 return Array.BinarySearch <T> (_items, 0, _size, item, comparer);
151 public int BinarySearch (int index, int count, T item, IComparer <T> comparer)
153 CheckRange (index, count);
154 return Array.BinarySearch <T> (_items, index, count, item, comparer);
159 Array.Clear (_items, 0, _items.Length);
164 public bool Contains (T item)
166 EqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
167 for (int i = 0; i < _size; i++)
168 if (equalityComparer.Equals (_items[i], item))
173 public List <TOutput> ConvertAll <TOutput> (Converter <T, TOutput> converter)
175 if (converter == null)
176 throw new ArgumentNullException ("converter");
177 List <TOutput> u = new List <TOutput> (_size);
178 foreach (T t in this)
179 u.Add (converter (t));
183 public void CopyTo (T [] array)
185 Array.Copy (_items, 0, array, 0, _size);
188 public void CopyTo (T [] array, int arrayIndex)
190 Array.Copy (_items, 0, array, arrayIndex, _size);
193 public void CopyTo (int index, T [] array, int arrayIndex, int count)
195 CheckRange (index, count);
196 Array.Copy (_items, index, array, arrayIndex, count);
199 public bool Exists (Predicate <T> match)
201 return FindIndex (match) != -1;
204 public T Find (Predicate <T> match)
206 int i = FindIndex (match);
207 return (i != -1) ? _items [i] : default (T);
209 void CheckMatch (Predicate <T> match)
212 throw new ArgumentNullException ("match");
215 public List <T> FindAll (Predicate <T> match)
217 this.CheckMatch (match);
218 if (this._size <= 0x10000) // <= 8 * 1024 * 8 (8k in stack)
219 return this.FindAllStackBits (match);
221 return this.FindAllList (match);
224 private List <T> FindAllStackBits (Predicate <T> match)
228 uint *bits = stackalloc uint [(this._size / 32) + 1];
231 uint bitmask = 0x80000000;
233 for (int i = 0; i < this._size; i++)
235 if (match (this._items [i]))
237 (*ptr) = (*ptr) | bitmask;
241 bitmask = bitmask >> 1;
245 bitmask = 0x80000000;
249 List <T> results = new List <T> (found);
250 bitmask = 0x80000000;
252 for (int i = 0; i < this._size; i++)
254 if (((*ptr) & bitmask) == bitmask)
255 results.Add (this._items [i]);
257 bitmask = bitmask >> 1;
261 bitmask = 0x80000000;
269 private List <T> FindAllList (Predicate <T> match)
271 List <T> results = new List <T> ();
272 for (int i = 0; i < this._size; i++)
273 if (match (this._items [i]))
274 results.Add (this._items [i]);
279 public int FindIndex (Predicate <T> match)
282 return GetIndex (0, _size, match);
285 public int FindIndex (int startIndex, Predicate <T> match)
288 CheckIndex (startIndex);
289 return GetIndex (startIndex, _size - startIndex, match);
291 public int FindIndex (int startIndex, int count, Predicate <T> match)
294 CheckRange (startIndex, count);
295 return GetIndex (startIndex, count, match);
297 int GetIndex (int startIndex, int count, Predicate <T> match)
299 for (int i = startIndex; i < startIndex + count; i ++)
300 if (match (_items [i]))
306 public T FindLast (Predicate <T> match)
309 int i = GetLastIndex (0, _size, match);
310 return i == -1 ? default (T) : this [i];
313 public int FindLastIndex (Predicate <T> match)
316 return GetLastIndex (0, _size, match);
319 public int FindLastIndex (int startIndex, Predicate <T> match)
322 CheckIndex (startIndex);
323 return GetLastIndex (0, startIndex + 1, match);
326 public int FindLastIndex (int startIndex, int count, Predicate <T> match)
329 int start = startIndex - count + 1;
330 CheckRange (start, count);
331 return GetLastIndex (start, count, match);
334 int GetLastIndex (int startIndex, int count, Predicate <T> match)
336 // unlike FindLastIndex, takes regular params for search range
337 for (int i = startIndex + count; i != startIndex;)
338 if (match (_items [--i]))
343 public void ForEach (Action <T> action)
346 throw new ArgumentNullException ("action");
347 foreach (T t in this)
351 public Enumerator GetEnumerator ()
353 return new Enumerator (this);
356 public List <T> GetRange (int index, int count)
358 CheckRange (index, count);
359 T [] tmpArray = new T [count];
360 Array.Copy (_items, index, tmpArray, 0, count);
361 return new List <T> (tmpArray, count);
364 public int IndexOf (T item)
366 return Array.IndexOf<T> (_items, item, 0, _size);
369 public int IndexOf (T item, int index)
372 return Array.IndexOf<T> (_items, item, index, _size - index);
375 public int IndexOf (T item, int index, int count)
378 throw new ArgumentOutOfRangeException ("index");
381 throw new ArgumentOutOfRangeException ("count");
383 if ((uint) index + (uint) count > (uint) _size)
384 throw new ArgumentOutOfRangeException ("index and count exceed length of list");
386 return Array.IndexOf<T> (_items, item, index, count);
389 void Shift (int start, int delta)
394 Array.Copy (_items, start, _items, start + delta, _size - start);
399 void CheckIndex (int index)
401 if (index < 0 || (uint) index > (uint) _size)
402 throw new ArgumentOutOfRangeException ("index");
405 public void Insert (int index, T item)
414 void CheckCollection (IEnumerable <T> collection)
416 if (collection == null)
417 throw new ArgumentNullException ("collection");
420 public void InsertRange (int index, IEnumerable <T> collection)
422 CheckCollection (collection);
424 if (collection == this) {
425 T[] buffer = new T[_size];
427 GrowIfNeeded (_size);
428 Shift (index, buffer.Length);
429 Array.Copy (buffer, 0, _items, index, buffer.Length);
431 ICollection <T> c = collection as ICollection <T>;
433 InsertCollection (index, c);
435 InsertEnumeration (index, collection);
440 void InsertCollection (int index, ICollection <T> collection)
442 int collectionCount = collection.Count;
443 GrowIfNeeded (collectionCount);
445 Shift (index, collectionCount);
446 collection.CopyTo (_items, index);
448 void InsertEnumeration (int index, IEnumerable <T> enumerable)
450 foreach (T t in enumerable)
454 public int LastIndexOf (T item)
456 return Array.LastIndexOf<T> (_items, item, _size - 1, _size);
459 public int LastIndexOf (T item, int index)
462 return Array.LastIndexOf<T> (_items, item, index, index + 1);
465 public int LastIndexOf (T item, int index, int count)
468 throw new ArgumentOutOfRangeException ("index", index, "index is negative");
471 throw new ArgumentOutOfRangeException ("count", count, "count is negative");
473 if (index - count + 1 < 0)
474 throw new ArgumentOutOfRangeException ("cound", count, "count is too large");
476 return Array.LastIndexOf<T> (_items, item, index, count);
479 public bool Remove (T item)
481 int loc = IndexOf (item);
488 // FIXME: this could probably be made faster.
489 public int RemoveAll (Predicate <T> match)
495 while ((index = GetIndex (index, _size - index, match)) != -1) {
500 Array.Clear (_items, _size, c);
504 public void RemoveAt (int index)
508 Array.Clear (_items, _size, 0);
512 public void RemoveRange (int index, int count)
514 CheckRange (index, count);
516 Shift (index, -count);
517 Array.Clear (_items, _size, count);
522 public void Reverse ()
524 Array.Reverse (_items, 0, _size);
527 public void Reverse (int index, int count)
529 CheckRange (index, count);
530 Array.Reverse (_items, index, count);
536 Array.Sort<T> (_items, 0, _size, Comparer <T>.Default);
539 public void Sort (IComparer <T> comparer)
541 Array.Sort<T> (_items, 0, _size, comparer);
545 public void Sort (Comparison <T> comparison)
547 Array.Sort<T> (_items, _size, comparison);
551 public void Sort (int index, int count, IComparer <T> comparer)
553 CheckRange (index, count);
554 Array.Sort<T> (_items, index, count, comparer);
558 public T [] ToArray ()
560 T [] t = new T [_size];
561 Array.Copy (_items, t, _size);
566 public void TrimExcess ()
571 public bool TrueForAll (Predicate <T> match)
575 foreach (T t in this)
582 public int Capacity {
584 return _items.Length;
587 if ((uint) value < (uint) _size)
588 throw new ArgumentOutOfRangeException ();
590 Array.Resize (ref _items, value);
595 get { return _size; }
598 public T this [int index] {
600 if ((uint) index >= (uint) _size)
601 throw new ArgumentOutOfRangeException ("index");
602 return _items [index];
606 if ((uint) index == (uint) _size)
607 throw new ArgumentOutOfRangeException ("index");
608 _items [index] = value;
612 #region Interface implementations.
613 IEnumerator <T> IEnumerable <T>.GetEnumerator ()
615 return GetEnumerator ();
618 void ICollection.CopyTo (Array array, int arrayIndex)
620 Array.Copy (_items, 0, array, arrayIndex, _size);
623 IEnumerator IEnumerable.GetEnumerator ()
625 return GetEnumerator ();
628 int IList.Add (object item)
632 } catch (InvalidCastException) {
633 throw new ArgumentException("item");
638 bool IList.Contains (object item)
641 return Contains ((T) item);
642 } catch (InvalidCastException) {
647 int IList.IndexOf (object item)
650 return IndexOf((T) item);
651 } catch (InvalidCastException) {
656 void IList.Insert (int index, object item)
658 // We need to check this first because, even if the
659 // item is null or not the correct type, we need to
660 // return an ArgumentOutOfRange exception if the
661 // index is out of range
664 Insert (index, (T) item);
665 } catch (InvalidCastException) {
666 throw new ArgumentException("item");
670 void IList.Remove (object item)
674 } catch (InvalidCastException) {
675 // Swallow the exception--if we
676 // can't cast to the correct type
677 // then we've already "succeeded"
678 // in removing the item from the
683 bool ICollection <T>.IsReadOnly {
684 get { return false; }
686 bool ICollection.IsSynchronized {
687 get { return false; }
690 object ICollection.SyncRoot {
693 bool IList.IsFixedSize {
694 get { return false; }
697 bool IList.IsReadOnly {
698 get { return false; }
701 object IList.this [int index] {
702 get { return this [index]; }
703 set { this [index] = (T) value; }
708 public struct Enumerator : IEnumerator <T>, IDisposable {
709 const int NOT_STARTED = -2;
711 // this MUST be -1, because we depend on it in move next.
712 // we just decr the size, so, 0 - 1 == FINISHED
713 const int FINISHED = -1;
719 internal Enumerator (List <T> l)
726 // for some fucked up reason, MSFT added a useless dispose to this class
727 // It means that in foreach, we must still do a try/finally. Broken, very
729 public void Dispose ()
734 public bool MoveNext ()
736 if (ver != l._version)
737 throw new InvalidOperationException ("Collection was modified;"
738 + "enumeration operation may not execute.");
740 if (idx == NOT_STARTED)
743 return idx != FINISHED && -- idx != FINISHED;
749 throw new InvalidOperationException ();
751 return l._items [l._size - 1 - idx];
755 void IEnumerator.Reset ()
757 if (ver != l._version)
758 throw new InvalidOperationException ("Collection was modified;"
759 + "enumeration operation may not execute.");
764 object IEnumerator.Current {
765 get { return Current; }