New test.
[mono.git] / mcs / class / corlib / System.Collections.Generic / List.cs
index 19671e63eace27537da84fcfa37374f953e46ce5..206bf821846f34495438cd909525660f0e68dfba 100644 (file)
@@ -1,15 +1,14 @@
-// -*- Mode: csharp; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
 //
 // System.Collections.Generic.List
 //
-// Author:
+// Authors:
+//    Ben Maurer (bmaurer@ximian.com)
 //    Martin Baulig (martin@ximian.com)
+//    Carlos Alberto Cortez (calberto.cortez@gmail.com)
+//    David Waite (mass@akuma.org)
 //
-// (C) 2004 Novell, Inc.
-//
-
-//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2005 David Waite
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 //
 
 #if NET_2_0
-using System;
-using System.Collections;
-using System.Runtime.InteropServices;
 
-namespace System.Collections.Generic
-{
-       [CLSCompliant(false)]
-       [ComVisible(false)]
-       public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>,
-               IList, ICollection, IEnumerable
-       {
-               protected int count;
-               protected int capacity;
-               protected T[] contents;
-               protected int modified;
+using System.Collections.ObjectModel;
+using System.Runtime.InteropServices;
 
+namespace System.Collections.Generic {
+       [Serializable]
+       public class List <T> : IList <T>, IList, ICollection {
+               T [] data;
+               int size;
+               int version;
+               
+               static readonly T [] EmptyArray = new T [0]; 
+               const int DefaultCapacity = 4;
+               
                public List ()
                {
+                       data = EmptyArray;
                }
-
+               
+               public List (IEnumerable <T> collection)
+               {
+                       CheckCollection (collection);
+
+                       // initialize to needed size (if determinable)
+                       ICollection <T> c = collection as ICollection <T>;
+                       if (c == null)
+                       {
+                               data = EmptyArray;
+                               AddEnumerable (collection);
+                       }
+                       else
+                       {
+                               data = new T [c.Count];
+                               AddCollection (c);
+                       }
+               }
+               
                public List (int capacity)
                {
-                       this.capacity = capacity;
-                       contents = new T [capacity];
+                       if (capacity < 0)
+                               throw new ArgumentOutOfRangeException ("capacity");
+                       data = new T [capacity];
                }
-
-               public List (ICollection collection)
-                       : this (collection.Count)
+               
+               internal List (T [] data, int size)
                {
-                       collection.CopyTo (contents, 0);
-                       count = collection.Count;
+                       this.data = data;
+                       this.size = size;
                }
-
-               protected void Resize (int size)
+               public void Add (T item)
                {
-                       if (size < capacity)
-                               return;
-
-                       if (size < 10)
-                               size = 10;
-
-                       T[] ncontents = new T [size];
-                       if (count > 0)
-                               Array.Copy (contents, 0, ncontents, 0, count);
-
-                       modified++;
-                       contents = ncontents;
-                       capacity = size;
+                       GrowIfNeeded (1);
+                       data [size ++] = item;
                }
-
-               public int Add (T item)
+               
+               void GrowIfNeeded (int newCount)
                {
-                       if (count >= capacity)
-                               Resize (2 * capacity);
-
-                       contents [count] = item;
-                       return count++;
+                       int minimumSize = size + newCount;
+                       if (minimumSize > data.Length)
+                               Capacity = Math.Max (Math.Max (Capacity * 2, DefaultCapacity), minimumSize);
                }
-
-               int IList.Add (object item)
+               
+               void CheckRange (int idx, int count)
                {
-                       return Add ((T) item);
+                       if (idx < 0)
+                               throw new ArgumentOutOfRangeException ("index");
+                       
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count");
+
+                       if ((uint) idx + (uint) count > (uint) size)
+                               throw new ArgumentException ("index and count exceed length of list");
+               }
+               
+               void AddCollection (ICollection <T> collection)
+               {
+                       int collectionCount = collection.Count;
+                       GrowIfNeeded (collectionCount);                  
+                       collection.CopyTo (data, size);
+                       size += collectionCount;
+               }
+               void AddEnumerable (IEnumerable <T> enumerable)
+               {
+                       foreach (T t in enumerable)
+                       {
+                               Add (t);
+                       }
                }
 
+               public void AddRange (IEnumerable <T> collection)
+               {
+                       CheckCollection (collection);
+                       
+                       ICollection <T> c = collection as ICollection <T>;
+                       if (c != null)
+                               AddCollection (c);
+                       else
+                               AddEnumerable (collection);
+               }
+               
+               public ReadOnlyCollection <T> AsReadOnly ()
+               {
+                       return new ReadOnlyCollection <T> (this);
+               }
+               
+               public int BinarySearch (T item)
+               {
+                       return Array.BinarySearch <T> (data, 0, size, item);
+               }
+               
+               public int BinarySearch (T item, IComparer <T> comparer)
+               {
+                       return Array.BinarySearch <T> (data, 0, size, item, comparer);
+               }
+               
+               public int BinarySearch (int index, int count, T item, IComparer <T> comparer)
+               {
+                       CheckRange (index, count);
+                       return Array.BinarySearch <T> (data, index, count, item, comparer);
+               }
+               
                public void Clear ()
                {
-                       count = 0;
+                       Array.Clear (data, 0, data.Length);
+                       size = 0;
                }
-
+               
                public bool Contains (T item)
                {
-                       for (int i = 0; i < count; i++)
-                               if (contents [i] == item)
+                       EqualityComparer<T> equalityComparer = EqualityComparer<T>.Default;
+                       for (int i = 0; i < size; i++)
+                               if (equalityComparer.Equals (data[i], item))
                                        return true;
-
                        return false;
                }
-
-               bool IList.Contains (object item)
+               
+               public List <TOutput> ConvertAll <TOutput> (Converter <T, TOutput> converter)
                {
-                       return Contains ((T) item);
+                       if (converter == null)
+                               throw new ArgumentNullException ("converter");
+                       List <TOutput> u = new List <TOutput> (size);
+                       foreach (T t in this)
+                               u.Add (converter (t));
+                       return u;
+               }
+               
+               public void CopyTo (T [] array)
+               {
+                       Array.Copy (data, 0, array, 0, size);
+               }
+               
+               public void CopyTo (T [] array, int arrayIndex)
+               {
+                       Array.Copy (data, 0, array, arrayIndex, size);
+               }
+               
+               public void CopyTo (int index, T [] array, int arrayIndex, int count)
+               {
+                       CheckRange (index, count);
+                       Array.Copy (data, index, array, arrayIndex, count);
                }
 
-               public int IndexOf (T item)
+               public bool Exists (Predicate <T> match)
                {
-                       for (int i = 0; i < count; i++)
-                               if (contents [i] == item)
+                       return FindIndex (match) != -1;
+               }
+               
+               public T Find (Predicate <T> match)
+               {
+                       int i = FindIndex (match);
+                       return (i != -1) ? data [i] : default (T);
+               }
+               void CheckMatch (Predicate <T> match)
+               {
+                       if (match == null)
+                               throw new ArgumentNullException ("match");
+               }
+               
+               // Maybe we could make this faster. For example, you could
+               // make a bit set with stackalloc for which elements to copy
+               // then you could size the array correctly.
+               public List <T> FindAll (Predicate <T> match)
+               {
+                       CheckMatch (match);
+                       List <T> f = new List <T> ();
+                       
+                       foreach (T t in this)
+                               if (match (t))
+                                       f.Add (t);
+                       
+                       return f;
+               }
+               
+               public int FindIndex (Predicate <T> match)
+               {
+                       CheckMatch (match);
+                       return GetIndex (0, size, match);
+               }
+               
+               public int FindIndex (int startIndex, Predicate <T> match)
+               {
+                       CheckMatch (match);
+                       CheckIndex (startIndex);
+                       return GetIndex (startIndex, size - startIndex, match);
+               }
+               public int FindIndex (int startIndex, int count, Predicate <T> match)
+               {
+                       CheckMatch (match);
+                       CheckRange (startIndex, count);
+                       return GetIndex (startIndex, count, match);
+               }
+               int GetIndex (int startIndex, int count, Predicate <T> match)
+               {
+                       for (int i = startIndex; i < startIndex + count; i ++)
+                               if (match (data [i]))
                                        return i;
-
+                               
                        return -1;
                }
-
-               int IList.IndexOf (object item)
+               
+               public T FindLast (Predicate <T> match)
                {
-                       return IndexOf ((T) item);
+                       CheckMatch (match);
+                       int i = GetLastIndex (0, size, match);
+                       return i == -1 ? default (T) : this [i];
+               }
+               
+               public int FindLastIndex (Predicate <T> match)
+               {
+                       CheckMatch (match);
+                       return GetLastIndex (0, size, match);
+               }
+               
+               public int FindLastIndex (int startIndex, Predicate <T> match)
+               {
+                       CheckMatch (match);
+                       CheckIndex (startIndex);
+                       return GetLastIndex (0, startIndex + 1, match);
+               }
+               
+               public int FindLastIndex (int startIndex, int count, Predicate <T> match)
+               {
+                       CheckMatch (match);
+                       int start = startIndex - count + 1;
+                       CheckRange (start, count);
+                       return GetLastIndex (start, count, match);
                }
 
-               public void Insert (int index, T item)
+               int GetLastIndex (int startIndex, int count, Predicate <T> match)
+               {
+                       // unlike FindLastIndex, takes regular params for search range
+                       for (int i = startIndex + count; i != startIndex;)
+                               if (match (data [--i]))
+                                       return i;
+                       return -1;      
+               }
+               
+               public void ForEach (Action <T> action)
+               {
+                       if (action == null)
+                               throw new ArgumentNullException ("action");
+                       foreach (T t in this)
+                               action (t);
+               }
+               
+               public Enumerator GetEnumerator ()
+               {
+                       return new Enumerator (this);
+               }
+               
+               public List <T> GetRange (int index, int count)
+               {
+                       CheckRange (index, count);
+                       T [] tmpArray = new T [count];
+                       Array.Copy (data, index, tmpArray, 0, count);
+                       return new List <T> (tmpArray, count);
+               }
+               
+               public int IndexOf (T item)
+               {
+                       return Array.IndexOf<T> (data, item, 0, size);
+               }
+               
+               public int IndexOf (T item, int index)
+               {
+                       CheckIndex (index);
+                       return Array.IndexOf<T> (data, item, index, size - index);
+               }
+               
+               public int IndexOf (T item, int index, int count)
                {
                        if (index < 0)
-                               throw new ArgumentException ();
-                       if (index > count)
-                               index = count;
+                               throw new ArgumentOutOfRangeException ("index");
+                       
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count");
 
-                       Resize (index);
-                       int rest = count - index;
-                       if (rest > 0)
-                               Array.Copy (contents, index, contents, index+1, rest);
-                       contents [index] = item;
+                       if ((uint) index + (uint) count > (uint) size)
+                               throw new ArgumentOutOfRangeException ("index and count exceed length of list");
+
+                       return Array.IndexOf<T> (data, item, index, count);
+               }
+               
+               void Shift (int start, int delta)
+               {
+                       if (delta < 0)
+                               start -= delta;
+                       
+                       Array.Copy (data, start, data, start + delta, size - start);
+                       
+                       size += delta;
                }
 
-               void IList.Insert (int index, object item)
+               void CheckIndex (int index)
                {
-                       Insert (index, (T) item);
+                       if (index < 0 || (uint) index > (uint) size)
+                               throw new ArgumentOutOfRangeException ("index");
+               }
+               
+               public void Insert (int index, T item)
+               {
+                       CheckIndex (index);                     
+                       GrowIfNeeded (1);
+                       Shift (index, 1);
+                       this [index] = item;
+                               
                }
 
-               public void Remove (T item)
+               void CheckCollection (IEnumerable <T> collection)
                {
-                       int index = IndexOf (item);
-                       if (index >= 0)
-                               RemoveAt (index);
+                       if (collection == null)
+                               throw new ArgumentNullException ("collection");
+               }
+               
+               public void InsertRange (int index, IEnumerable <T> collection)
+               {
+                       CheckCollection (collection);
+                       CheckIndex (index);
+                       ICollection <T> c = collection as ICollection <T>;
+                       if (c != null)
+                               InsertCollection (index, c);
+                       else
+                               InsertEnumeration (index, collection);
                }
 
-               void IList.Remove (object item)
+               void InsertCollection (int index, ICollection <T> collection)
                {
-                       Remove ((T) item);
+                       int collectionCount = collection.Count;
+                       GrowIfNeeded (collectionCount);
+                       
+                       Shift (index, collectionCount);
+                       collection.CopyTo (data, index);
+               }
+               void InsertEnumeration (int index, IEnumerable <T> enumerable)
+               {
+                       foreach (T t in enumerable)
+                               Insert (index++, t);            
                }
 
-               public void RemoveAt (int index)
+               public int LastIndexOf (T item)
+               {
+                       return Array.LastIndexOf<T> (data, item, size - 1, size);
+               }
+               
+               public int LastIndexOf (T item, int index)
                {
-                       if ((index < 0) || (count == 0))
-                               throw new ArgumentException ();
-                       if (index > count)
-                               index = count;
+                       CheckIndex (index);
+                       return Array.LastIndexOf<T> (data, item, index, index + 1);
+               }
+               
+               public int LastIndexOf (T item, int index, int count)
+               {
+                       if (index < 0)
+                               throw new ArgumentOutOfRangeException ("index", index, "index is negative");
 
-                       int rest = count - index;
-                       if (rest > 0)
-                               Array.Copy (contents, index+1, contents, index, rest);
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count", count, "count is negative");
 
-                       count--;
+                       if (index - count + 1 < 0)
+                               throw new ArgumentOutOfRangeException ("cound", count, "count is too large");
+
+                       return Array.LastIndexOf<T> (data, item, index, count);
+               }
+               
+               public bool Remove (T item)
+               {
+                       int loc = IndexOf (item);
+                       if (loc != -1)
+                               RemoveAt (loc);
+                       
+                       return loc != -1;
                }
+               
+               [MonoTODO ("I can make it faster than this...")]
+               public int RemoveAll (Predicate <T> match)
+               {
+                       CheckMatch (match);
 
-               public bool IsFixedSize {
-                       get {
-                               return false;
+                       int index = 0;
+                       int c = 0;
+                       while ((index = GetIndex (index, size - index, match)) != -1) {
+                               RemoveAt (index);
+                               c ++;
                        }
+                       
+                       Array.Clear (data, size, c);
+                       return c;
+               }
+               
+               public void RemoveAt (int index)
+               {
+                       CheckIndex (index);
+                       Shift (index, -1);
+                       Array.Clear (data, size, 0);
+               }
+               
+               public void RemoveRange (int index, int count)
+               {
+                       CheckRange (index, count);
+                       Shift (index, -count);
+                       Array.Clear (data, size, count);
+               }
+               
+               public void Reverse ()
+               {
+                       Array.Reverse (data, 0, size);
+               }
+               public void Reverse (int index, int count)
+               {
+                       CheckRange (index, count);
+                       Array.Reverse (data, index, count);
+               }
+               
+               public void Sort ()
+               {
+                       Array.Sort<T> (data, 0, size, Comparer <T>.Default);
+               }
+               public void Sort (IComparer <T> comparer)
+               {
+                       Array.Sort<T> (data, 0, size, comparer);
                }
 
-               public bool IsReadOnly {
-                       get {
-                               return false;
-                       }
+               public void Sort (Comparison <T> comparison)
+               {
+                       Array.Sort<T> (data, size, comparison);
+               }
+               
+               public void Sort (int index, int count, IComparer <T> comparer)
+               {
+                       CheckRange (index, count);
+                       Array.Sort<T> (data, index, count, comparer);
                }
 
-               public T this [int index] {
-                       get {
-                               return contents [index];
-                       }
+               public T [] ToArray ()
+               {
+                       T [] t = new T [size];
+                       Array.Copy (data, t, size);
+                       
+                       return t;
+               }
+               
+               public void TrimExcess ()
+               {
+                       Capacity = size;
+               }
+               
+               public bool TrueForAll (Predicate <T> match)
+               {
+                       CheckMatch (match);
 
+                       foreach (T t in this)
+                               if (!match (t))
+                                       return false;
+                               
+                       return true;
+               }
+               
+               public int Capacity {
+                       get { 
+                               return data.Length;
+                       }
                        set {
-                               contents [index] = value;
+                               if ((uint) value < (uint) size)
+                                       throw new ArgumentOutOfRangeException ();
+                               
+                               Array.Resize (ref data, value);
                        }
                }
-
-               object IList.this [int index] {
+               
+               public int Count {
+                       get { return size; }
+               }
+               
+               public T this [int index] {
                        get {
-                               return contents [index];
+                               if ((uint) index >= (uint) size)
+                                       throw new ArgumentOutOfRangeException ("index");
+                               return data [index];
                        }
-
                        set {
-                               // contents [index] = (T) value;
+                               CheckIndex (index);
+                               data [index] = value;
                        }
                }
-
-               public void CopyTo (T[] array, int arrayIndex)
+               
+#region Interface implementations.
+               IEnumerator <T> IEnumerable <T>.GetEnumerator ()
                {
-                       if (count > 0)
-                               Array.Copy (contents, 0, array, arrayIndex, count);
+                       return GetEnumerator ();
                }
-
+               
                void ICollection.CopyTo (Array array, int arrayIndex)
                {
-                       if (count > 0)
-                               Array.Copy (contents, 0, array, arrayIndex, count);
+                       Array.Copy (data, 0, array, arrayIndex, size);
                }
-
-               public int Count {
-                       get {
-                               return count;
-                       }
+               
+               IEnumerator IEnumerable.GetEnumerator ()
+               {
+                       return GetEnumerator ();
                }
-
-               public bool IsSynchronized {
-                       get { return false; }
+               
+               int IList.Add (object item)
+               {
+                       Add ((T) item);
+                       return size - 1;
                }
-
-               public object SyncRoot {
-                       get { return this; }
+               
+               bool IList.Contains (object item)
+               {
+                       return Contains ((T) item);
                }
-
-               public IEnumerator<T> GetEnumerator ()
+               
+               int IList.IndexOf (object item)
                {
-                       for (int i = 0; i < count; i++)
-                               yield return contents [i];
+                       return IndexOf ((T) item);
                }
-
-               IEnumerator IEnumerable.GetEnumerator ()
+               
+               void IList.Insert (int index, object item)
                {
-                       for (int i = 0; i < count; i++)
-                               yield return contents [i];
+                       Insert (index, (T) item);
+               }
+               
+               void IList.Remove (object item)
+               {
+                       Remove ((T) item);
+               }
+               
+               bool ICollection <T>.IsReadOnly {
+                       get { return false; }
+               }
+               bool ICollection.IsSynchronized {
+                       get { return false; }
+               }
+               
+               object ICollection.SyncRoot {
+                       get { return this; }
+               }
+               bool IList.IsFixedSize {
+                       get { return false; }
+               }
+               
+               bool IList.IsReadOnly {
+                       get { return false; }
+               }
+               
+               object IList.this [int index] {
+                       get { return this [index]; }
+                       set { this [index] = (T) value; }
+               }
+#endregion
+                               
+               [Serializable]
+               public struct Enumerator : IEnumerator <T>, IDisposable {
+                       const int NOT_STARTED = -2;
+                       
+                       // this MUST be -1, because we depend on it in move next.
+                       // we just decr the size, so, 0 - 1 == FINISHED
+                       const int FINISHED = -1;
+                       
+                       List <T> l;
+                       int idx;
+                       int ver;
+                       
+                       internal Enumerator (List <T> l)
+                       {
+                               this.l = l;
+                               idx = NOT_STARTED;
+                               ver = l.version;
+                       }
+                       
+                       // for some fucked up reason, MSFT added a useless dispose to this class
+                       // It means that in foreach, we must still do a try/finally. Broken, very
+                       // broken.
+                       public void Dispose ()
+                       {
+                               idx = NOT_STARTED;
+                       }
+                       
+                       public bool MoveNext ()
+                       {
+                               if (ver != l.version)
+                                       throw new InvalidOperationException ();
+                               
+                               if (idx == NOT_STARTED)
+                                       idx = l.size;
+                               
+                               return idx != FINISHED && -- idx != FINISHED;
+                       }
+                       
+                       public T Current {
+                               get {
+                                       if (idx < 0)
+                                               throw new InvalidOperationException ();
+                                       
+                                       return l.data [l.size - 1 - idx];
+                               }
+                       }
+                       
+                       void IEnumerator.Reset ()
+                       {
+                               if (ver != l.version)
+                                       throw new InvalidOperationException ();
+                               
+                               idx = NOT_STARTED;
+                       }
+                       
+                       object IEnumerator.Current {
+                               get { return Current; }
+                       }
                }
        }
 }