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 data = new T [c.Count];
71 public List (int capacity)
74 throw new ArgumentOutOfRangeException ("capacity");
75 data = new T [capacity];
78 internal List (T [] data, int size)
83 public void Add (T item)
86 data [size ++] = item;
89 void GrowIfNeeded (int newCount)
91 int minimumSize = size + newCount;
92 if (minimumSize > data.Length)
93 Capacity = Math.Max (Math.Max (Capacity * 2, DefaultCapacity), minimumSize);
96 void CheckRange (int idx, int count)
99 throw new ArgumentOutOfRangeException ("index");
102 throw new ArgumentOutOfRangeException ("count");
104 if ((uint) idx + (uint) count > (uint) size)
105 throw new ArgumentException ("index and count exceed length of list");
108 void AddCollection (ICollection <T> collection)
110 int collectionCount = collection.Count;
111 GrowIfNeeded (collectionCount);
112 collection.CopyTo (data, size);
113 size += collectionCount;
115 void AddEnumerable (IEnumerable <T> enumerable)
117 foreach (T t in enumerable)
123 public void AddRange (IEnumerable <T> collection)
125 CheckCollection (collection);
127 ICollection <T> c = collection as ICollection <T>;
131 AddEnumerable (collection);
134 public ReadOnlyCollection <T> AsReadOnly ()
136 return new ReadOnlyCollection <T> (this);
139 public int BinarySearch (T item)
141 return Array.BinarySearch <T> (data, 0, size, item);
144 public int BinarySearch (T item, IComparer <T> comparer)
146 return Array.BinarySearch <T> (data, 0, size, item, comparer);
149 public int BinarySearch (int index, int count, T item, IComparer <T> comparer)
151 CheckRange (index, count);
152 return Array.BinarySearch <T> (data, index, count, item, comparer);
157 Array.Clear (data, 0, data.Length);
161 public bool Contains (T item)
163 EqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
164 for (int i = 0; i < size; i++)
165 if (equalityComparer.Equals (data[i], item))
170 public List <TOutput> ConvertAll <TOutput> (Converter <T, TOutput> converter)
172 if (converter == null)
173 throw new ArgumentNullException ("converter");
174 List <TOutput> u = new List <TOutput> (size);
175 foreach (T t in this)
176 u.Add (converter (t));
180 public void CopyTo (T [] array)
182 Array.Copy (data, 0, array, 0, size);
185 public void CopyTo (T [] array, int arrayIndex)
187 Array.Copy (data, 0, array, arrayIndex, size);
190 public void CopyTo (int index, T [] array, int arrayIndex, int count)
192 CheckRange (index, count);
193 Array.Copy (data, index, array, arrayIndex, count);
196 public bool Exists (Predicate <T> match)
198 return FindIndex (match) != -1;
201 public T Find (Predicate <T> match)
203 int i = FindIndex (match);
204 return (i != -1) ? data [i] : default (T);
206 void CheckMatch (Predicate <T> match)
209 throw new ArgumentNullException ("match");
212 // Maybe we could make this faster. For example, you could
213 // make a bit set with stackalloc for which elements to copy
214 // then you could size the array correctly.
215 public List <T> FindAll (Predicate <T> match)
218 List <T> f = new List <T> ();
220 foreach (T t in this)
227 public int FindIndex (Predicate <T> match)
230 return GetIndex (0, size, match);
233 public int FindIndex (int startIndex, Predicate <T> match)
236 CheckIndex (startIndex);
237 return GetIndex (startIndex, size - startIndex, match);
239 public int FindIndex (int startIndex, int count, Predicate <T> match)
242 CheckRange (startIndex, count);
243 return GetIndex (startIndex, count, match);
245 int GetIndex (int startIndex, int count, Predicate <T> match)
247 for (int i = startIndex; i < startIndex + count; i ++)
248 if (match (data [i]))
254 public T FindLast (Predicate <T> match)
257 int i = GetLastIndex (0, size, match);
258 return i == -1 ? default (T) : this [i];
261 public int FindLastIndex (Predicate <T> match)
264 return GetLastIndex (0, size, match);
267 public int FindLastIndex (int startIndex, Predicate <T> match)
270 CheckIndex (startIndex);
271 return GetLastIndex (0, startIndex + 1, match);
274 public int FindLastIndex (int startIndex, int count, Predicate <T> match)
277 int start = startIndex - count + 1;
278 CheckRange (start, count);
279 return GetLastIndex (start, count, match);
282 int GetLastIndex (int startIndex, int count, Predicate <T> match)
284 // unlike FindLastIndex, takes regular params for search range
285 for (int i = startIndex + count; i != startIndex;)
286 if (match (data [--i]))
291 public void ForEach (Action <T> action)
294 throw new ArgumentNullException ("action");
295 foreach (T t in this)
299 public Enumerator GetEnumerator ()
301 return new Enumerator (this);
304 public List <T> GetRange (int index, int count)
306 CheckRange (index, count);
307 T [] tmpArray = new T [count];
308 Array.Copy (data, index, tmpArray, 0, count);
309 return new List <T> (tmpArray, count);
312 public int IndexOf (T item)
314 return Array.IndexOf<T> (data, item, 0, size);
317 public int IndexOf (T item, int index)
320 return Array.IndexOf<T> (data, item, index, size - index);
323 public int IndexOf (T item, int index, int count)
326 throw new ArgumentOutOfRangeException ("index");
329 throw new ArgumentOutOfRangeException ("count");
331 if ((uint) index + (uint) count > (uint) size)
332 throw new ArgumentOutOfRangeException ("index and count exceed length of list");
334 return Array.IndexOf<T> (data, item, index, count);
337 void Shift (int start, int delta)
342 Array.Copy (data, start, data, start + delta, size - start);
347 void CheckIndex (int index)
349 if (index < 0 || (uint) index > (uint) size)
350 throw new ArgumentOutOfRangeException ("index");
353 public void Insert (int index, T item)
362 void CheckCollection (IEnumerable <T> collection)
364 if (collection == null)
365 throw new ArgumentNullException ("collection");
368 public void InsertRange (int index, IEnumerable <T> collection)
370 CheckCollection (collection);
372 ICollection <T> c = collection as ICollection <T>;
374 InsertCollection (index, c);
376 InsertEnumeration (index, collection);
379 void InsertCollection (int index, ICollection <T> collection)
381 int collectionCount = collection.Count;
382 GrowIfNeeded (collectionCount);
384 Shift (index, collectionCount);
385 collection.CopyTo (data, index);
387 void InsertEnumeration (int index, IEnumerable <T> enumerable)
389 foreach (T t in enumerable)
393 public int LastIndexOf (T item)
395 return Array.LastIndexOf<T> (data, item, size - 1, size);
398 public int LastIndexOf (T item, int index)
401 return Array.LastIndexOf<T> (data, item, index, index + 1);
404 public int LastIndexOf (T item, int index, int count)
407 throw new ArgumentOutOfRangeException ("index", index, "index is negative");
410 throw new ArgumentOutOfRangeException ("count", count, "count is negative");
412 if (index - count + 1 < 0)
413 throw new ArgumentOutOfRangeException ("cound", count, "count is too large");
415 return Array.LastIndexOf<T> (data, item, index, count);
418 public bool Remove (T item)
420 int loc = IndexOf (item);
427 [MonoTODO ("I can make it faster than this...")]
428 public int RemoveAll (Predicate <T> match)
434 while ((index = GetIndex (index, size - index, match)) != -1) {
439 Array.Clear (data, size, c);
443 public void RemoveAt (int index)
447 Array.Clear (data, size, 0);
450 public void RemoveRange (int index, int count)
452 CheckRange (index, count);
453 Shift (index, -count);
454 Array.Clear (data, size, count);
457 public void Reverse ()
459 Array.Reverse (data, 0, size);
461 public void Reverse (int index, int count)
463 CheckRange (index, count);
464 Array.Reverse (data, index, count);
469 Array.Sort<T> (data, 0, size, Comparer <T>.Default);
471 public void Sort (IComparer <T> comparer)
473 Array.Sort<T> (data, 0, size, comparer);
476 public void Sort (Comparison <T> comparison)
478 Array.Sort<T> (data, size, comparison);
481 public void Sort (int index, int count, IComparer <T> comparer)
483 CheckRange (index, count);
484 Array.Sort<T> (data, index, count, comparer);
487 public T [] ToArray ()
489 T [] t = new T [size];
490 Array.Copy (data, t, size);
495 public void TrimExcess ()
500 public bool TrueForAll (Predicate <T> match)
504 foreach (T t in this)
511 public int Capacity {
516 if ((uint) value < (uint) size)
517 throw new ArgumentOutOfRangeException ();
519 Array.Resize (ref data, value);
527 public T this [int index] {
529 if ((uint) index >= (uint) size)
530 throw new ArgumentOutOfRangeException ("index");
535 data [index] = value;
539 #region Interface implementations.
540 IEnumerator <T> IEnumerable <T>.GetEnumerator ()
542 return GetEnumerator ();
545 void ICollection.CopyTo (Array array, int arrayIndex)
547 Array.Copy (data, 0, array, arrayIndex, size);
550 IEnumerator IEnumerable.GetEnumerator ()
552 return GetEnumerator ();
555 int IList.Add (object item)
561 bool IList.Contains (object item)
563 return Contains ((T) item);
566 int IList.IndexOf (object item)
568 return IndexOf ((T) item);
571 void IList.Insert (int index, object item)
573 Insert (index, (T) item);
576 void IList.Remove (object item)
581 bool ICollection <T>.IsReadOnly {
582 get { return false; }
584 bool ICollection.IsSynchronized {
585 get { return false; }
588 object ICollection.SyncRoot {
591 bool IList.IsFixedSize {
592 get { return false; }
595 bool IList.IsReadOnly {
596 get { return false; }
599 object IList.this [int index] {
600 get { return this [index]; }
601 set { this [index] = (T) value; }
606 public struct Enumerator : IEnumerator <T>, IDisposable {
607 const int NOT_STARTED = -2;
609 // this MUST be -1, because we depend on it in move next.
610 // we just decr the size, so, 0 - 1 == FINISHED
611 const int FINISHED = -1;
617 internal Enumerator (List <T> l)
624 // for some fucked up reason, MSFT added a useless dispose to this class
625 // It means that in foreach, we must still do a try/finally. Broken, very
627 public void Dispose ()
632 public bool MoveNext ()
634 if (ver != l.version)
635 throw new InvalidOperationException ();
637 if (idx == NOT_STARTED)
640 return idx != FINISHED && -- idx != FINISHED;
646 throw new InvalidOperationException ();
648 return l.data [l.size - 1 - idx];
652 void IEnumerator.Reset ()
654 if (ver != l.version)
655 throw new InvalidOperationException ();
660 object IEnumerator.Current {
661 get { return Current; }