diff options
Diffstat (limited to 'src/mscorlib/src/System/Collections/Generic/List.cs')
-rw-r--r-- | src/mscorlib/src/System/Collections/Generic/List.cs | 1120 |
1 files changed, 1120 insertions, 0 deletions
diff --git a/src/mscorlib/src/System/Collections/Generic/List.cs b/src/mscorlib/src/System/Collections/Generic/List.cs new file mode 100644 index 0000000000..ae3356d372 --- /dev/null +++ b/src/mscorlib/src/System/Collections/Generic/List.cs @@ -0,0 +1,1120 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** +** +** +** Purpose: Implements a generic, dynamically sized list as an +** array. +** +** +===========================================================*/ +namespace System.Collections.Generic { + + using System; + using System.Runtime; + using System.Runtime.Versioning; + using System.Diagnostics; + using System.Diagnostics.Contracts; + using System.Collections.ObjectModel; + using System.Security.Permissions; + + // Implements a variable-size List that uses an array of objects to store the + // elements. A List has a capacity, which is the allocated length + // of the internal array. As elements are added to a List, the capacity + // of the List is automatically increased as required by reallocating the + // internal array. + // + [DebuggerTypeProxy(typeof(Mscorlib_CollectionDebugView<>))] + [DebuggerDisplay("Count = {Count}")] + [Serializable] + public class List<T> : IList<T>, System.Collections.IList, IReadOnlyList<T> + { + private const int _defaultCapacity = 4; + + private T[] _items; + [ContractPublicPropertyName("Count")] + private int _size; + private int _version; + [NonSerialized] + private Object _syncRoot; + + static readonly T[] _emptyArray = new T[0]; + + // Constructs a List. The list is initially empty and has a capacity + // of zero. Upon adding the first element to the list the capacity is + // increased to _defaultCapacity, and then increased in multiples of two + // as required. + public List() { + _items = _emptyArray; + } + + // Constructs a List with a given initial capacity. The list is + // initially empty, but will have room for the given number of elements + // before any reallocations are required. + // + public List(int capacity) { + if (capacity < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + Contract.EndContractBlock(); + + if (capacity == 0) + _items = _emptyArray; + else + _items = new T[capacity]; + } + + // Constructs a List, copying the contents of the given collection. The + // size and capacity of the new list will both be equal to the size of the + // given collection. + // + public List(IEnumerable<T> collection) { + if (collection==null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); + Contract.EndContractBlock(); + + ICollection<T> c = collection as ICollection<T>; + if( c != null) { + int count = c.Count; + if (count == 0) + { + _items = _emptyArray; + } + else { + _items = new T[count]; + c.CopyTo(_items, 0); + _size = count; + } + } + else { + _size = 0; + _items = _emptyArray; + // This enumerable could be empty. Let Add allocate a new array, if needed. + // Note it will also go to _defaultCapacity first, not 1, then 2, etc. + + using(IEnumerator<T> en = collection.GetEnumerator()) { + while(en.MoveNext()) { + Add(en.Current); + } + } + } + } + + // Gets and sets the capacity of this list. The capacity is the size of + // the internal array used to hold items. When set, the internal + // array of the list is reallocated to the given capacity. + // + public int Capacity { + get { + Contract.Ensures(Contract.Result<int>() >= 0); + return _items.Length; + } + set { + if (value < _size) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity); + } + Contract.EndContractBlock(); + + if (value != _items.Length) { + if (value > 0) { + T[] newItems = new T[value]; + if (_size > 0) { + Array.Copy(_items, 0, newItems, 0, _size); + } + _items = newItems; + } + else { + _items = _emptyArray; + } + } + } + } + + // Read-only property describing how many elements are in the List. + public int Count { + get { + Contract.Ensures(Contract.Result<int>() >= 0); + return _size; + } + } + + bool System.Collections.IList.IsFixedSize { + get { return false; } + } + + + // Is this List read-only? + bool ICollection<T>.IsReadOnly { + get { return false; } + } + + bool System.Collections.IList.IsReadOnly { + get { return false; } + } + + // Is this List synchronized (thread-safe)? + bool System.Collections.ICollection.IsSynchronized { + get { return false; } + } + + // Synchronization root for this object. + Object System.Collections.ICollection.SyncRoot { + get { + if( _syncRoot == null) { + System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null); + } + return _syncRoot; + } + } + // Sets or Gets the element at the given index. + // + public T this[int index] { + get { + // Following trick can reduce the range check by one + if ((uint) index >= (uint)_size) { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + Contract.EndContractBlock(); + return _items[index]; + } + + set { + if ((uint) index >= (uint)_size) { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + Contract.EndContractBlock(); + _items[index] = value; + _version++; + } + } + + private static bool IsCompatibleObject(object value) { + // Non-null values are fine. Only accept nulls if T is a class or Nullable<U>. + // Note that default(T) is not equal to null for value types except when T is Nullable<U>. + return ((value is T) || (value == null && default(T) == null)); + } + + Object System.Collections.IList.this[int index] { + get { + return this[index]; + } + set { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(value, ExceptionArgument.value); + + try { + this[index] = (T)value; + } + catch (InvalidCastException) { + ThrowHelper.ThrowWrongValueTypeArgumentException(value, typeof(T)); + } + } + } + + // Adds the given object to the end of this list. The size of the list is + // increased by one. If required, the capacity of the list is doubled + // before adding the new element. + // + public void Add(T item) { + if (_size == _items.Length) EnsureCapacity(_size + 1); + _items[_size++] = item; + _version++; + } + + int System.Collections.IList.Add(Object item) + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item); + + try { + Add((T) item); + } + catch (InvalidCastException) { + ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); + } + + return Count - 1; + } + + + // Adds the elements of the given collection to the end of this list. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. + // + public void AddRange(IEnumerable<T> collection) { + Contract.Ensures(Count >= Contract.OldValue(Count)); + + InsertRange(_size, collection); + } + + public ReadOnlyCollection<T> AsReadOnly() { + Contract.Ensures(Contract.Result<ReadOnlyCollection<T>>() != null); + return new ReadOnlyCollection<T>(this); + } + + // Searches a section of the list for a given element using a binary search + // algorithm. Elements of the list are compared to the search value using + // the given IComparer interface. If comparer is null, elements of + // the list are compared to the search value using the IComparable + // interface, which in that case must be implemented by all elements of the + // list and the given search value. This method assumes that the given + // section of the list is already sorted; if this is not the case, the + // result will be incorrect. + // + // The method returns the index of the given value in the list. If the + // list does not contain the given value, the method returns a negative + // integer. The bitwise complement operator (~) can be applied to a + // negative result to produce the index of the first element (if any) that + // is larger than the given search value. This is also the index at which + // the search value should be inserted into the list in order for the list + // to remain sorted. + // + // The method uses the Array.BinarySearch method to perform the + // search. + // + public int BinarySearch(int index, int count, T item, IComparer<T> comparer) { + if (index < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + if (count < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + Contract.Ensures(Contract.Result<int>() <= index + count); + Contract.EndContractBlock(); + + return Array.BinarySearch<T>(_items, index, count, item, comparer); + } + + public int BinarySearch(T item) + { + Contract.Ensures(Contract.Result<int>() <= Count); + return BinarySearch(0, Count, item, null); + } + + public int BinarySearch(T item, IComparer<T> comparer) + { + Contract.Ensures(Contract.Result<int>() <= Count); + return BinarySearch(0, Count, item, comparer); + } + + + // Clears the contents of List. + public void Clear() { + if (_size > 0) + { + Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references. + _size = 0; + } + _version++; + } + + // Contains returns true if the specified element is in the List. + // It does a linear, O(n) search. Equality is determined by calling + // EqualityComparer<T>.Default.Equals(). + + public bool Contains(T item) + { + // PERF: IndexOf calls Array.IndexOf, which internally + // calls EqualityComparer<T>.Default.IndexOf, which + // is specialized for different types. This + // boosts performance since instead of making a + // virtual method call each iteration of the loop, + // via EqualityComparer<T>.Default.Equals, we + // only make one virtual call to EqualityComparer.IndexOf. + + return _size != 0 && IndexOf(item) != -1; + } + + bool System.Collections.IList.Contains(Object item) + { + if(IsCompatibleObject(item)) { + return Contains((T) item); + } + return false; + } + + public List<TOutput> ConvertAll<TOutput>(Converter<T,TOutput> converter) { + if( converter == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter); + } + + Contract.EndContractBlock(); + + List<TOutput> list = new List<TOutput>(_size); + for( int i = 0; i< _size; i++) { + list._items[i] = converter(_items[i]); + } + list._size = _size; + return list; + } + + // Copies this List into array, which must be of a + // compatible array type. + // + public void CopyTo(T[] array) { + CopyTo(array, 0); + } + + // Copies this List into array, which must be of a + // compatible array type. + // + void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) { + if ((array != null) && (array.Rank != 1)) { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); + } + Contract.EndContractBlock(); + + try { + // Array.Copy will check for NULL. + Array.Copy(_items, 0, array, arrayIndex, _size); + } + catch(ArrayTypeMismatchException){ + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType); + } + } + + // Copies a section of this list to the given array at the given index. + // + // The method uses the Array.Copy method to copy the elements. + // + public void CopyTo(int index, T[] array, int arrayIndex, int count) { + if (_size - index < count) { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + } + Contract.EndContractBlock(); + + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, index, array, arrayIndex, count); + } + + public void CopyTo(T[] array, int arrayIndex) { + // Delegate rest of error checking to Array.Copy. + Array.Copy(_items, 0, array, arrayIndex, _size); + } + + // Ensures that the capacity of this list is at least the given minimum + // value. If the currect capacity of the list is less than min, the + // capacity is increased to twice the current capacity or to min, + // whichever is larger. + private void EnsureCapacity(int min) { + if (_items.Length < min) { + int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2; + // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. + // Note that this check works even when _items.Length overflowed thanks to the (uint) cast + if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; + if (newCapacity < min) newCapacity = min; + Capacity = newCapacity; + } + } + + public bool Exists(Predicate<T> match) { + return FindIndex(match) != -1; + } + + public T Find(Predicate<T> match) { + if( match == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + Contract.EndContractBlock(); + + for(int i = 0 ; i < _size; i++) { + if(match(_items[i])) { + return _items[i]; + } + } + return default(T); + } + + public List<T> FindAll(Predicate<T> match) { + if( match == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + Contract.EndContractBlock(); + + List<T> list = new List<T>(); + for(int i = 0 ; i < _size; i++) { + if(match(_items[i])) { + list.Add(_items[i]); + } + } + return list; + } + + public int FindIndex(Predicate<T> match) { + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < Count); + return FindIndex(0, _size, match); + } + + public int FindIndex(int startIndex, Predicate<T> match) { + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < startIndex + Count); + return FindIndex(startIndex, _size - startIndex, match); + } + + public int FindIndex(int startIndex, int count, Predicate<T> match) { + if( (uint)startIndex > (uint)_size ) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + } + + if (count < 0 || startIndex > _size - count) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count); + } + + if( match == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < startIndex + count); + Contract.EndContractBlock(); + + int endIndex = startIndex + count; + for( int i = startIndex; i < endIndex; i++) { + if( match(_items[i])) return i; + } + return -1; + } + + public T FindLast(Predicate<T> match) { + if( match == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + Contract.EndContractBlock(); + + for(int i = _size - 1 ; i >= 0; i--) { + if(match(_items[i])) { + return _items[i]; + } + } + return default(T); + } + + public int FindLastIndex(Predicate<T> match) { + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < Count); + return FindLastIndex(_size - 1, _size, match); + } + + public int FindLastIndex(int startIndex, Predicate<T> match) { + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() <= startIndex); + return FindLastIndex(startIndex, startIndex + 1, match); + } + + public int FindLastIndex(int startIndex, int count, Predicate<T> match) { + if( match == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() <= startIndex); + Contract.EndContractBlock(); + + if(_size == 0) { + // Special case for 0 length List + if( startIndex != -1) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + } + } + else { + // Make sure we're not out of range + if ( (uint)startIndex >= (uint)_size) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index); + } + } + + // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. + if (count < 0 || startIndex - count + 1 < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count); + } + + int endIndex = startIndex - count; + for( int i = startIndex; i > endIndex; i--) { + if( match(_items[i])) { + return i; + } + } + return -1; + } + + public void ForEach(Action<T> action) { + if( action == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action); + } + Contract.EndContractBlock(); + + int version = _version; + + for(int i = 0 ; i < _size; i++) { + if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) { + break; + } + action(_items[i]); + } + + if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + // Returns an enumerator for this list with the given + // permission for removal of elements. If modifications made to the list + // while an enumeration is in progress, the MoveNext and + // GetObject methods of the enumerator will throw an exception. + // + public Enumerator GetEnumerator() { + return new Enumerator(this); + } + + /// <internalonly/> + IEnumerator<T> IEnumerable<T>.GetEnumerator() { + return new Enumerator(this); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return new Enumerator(this); + } + + public List<T> GetRange(int index, int count) { + if (index < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (count < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) { + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + } + Contract.Ensures(Contract.Result<List<T>>() != null); + Contract.EndContractBlock(); + + List<T> list = new List<T>(count); + Array.Copy(_items, index, list._items, 0, count); + list._size = count; + return list; + } + + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards from beginning to end. + // The elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item) { + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < Count); + return Array.IndexOf(_items, item, 0, _size); + } + + int System.Collections.IList.IndexOf(Object item) + { + if(IsCompatibleObject(item)) { + return IndexOf((T)item); + } + return -1; + } + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards, starting at index + // index and ending at count number of elements. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item, int index) { + if (index > _size) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < Count); + Contract.EndContractBlock(); + return Array.IndexOf(_items, item, index, _size - index); + } + + // Returns the index of the first occurrence of a given value in a range of + // this list. The list is searched forwards, starting at index + // index and upto count number of elements. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.IndexOf method to perform the + // search. + // + public int IndexOf(T item, int index, int count) { + if (index > _size) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); + + if (count <0 || index > _size - count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_Count); + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < Count); + Contract.EndContractBlock(); + + return Array.IndexOf(_items, item, index, count); + } + + // Inserts an element into this list at a given index. The size of the list + // is increased by one. If required, the capacity of the list is doubled + // before inserting the new element. + // + public void Insert(int index, T item) { + // Note that insertions at the end are legal. + if ((uint) index > (uint)_size) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert); + } + Contract.EndContractBlock(); + if (_size == _items.Length) EnsureCapacity(_size + 1); + if (index < _size) { + Array.Copy(_items, index, _items, index + 1, _size - index); + } + _items[index] = item; + _size++; + _version++; + } + + void System.Collections.IList.Insert(int index, Object item) + { + ThrowHelper.IfNullAndNullsAreIllegalThenThrow<T>(item, ExceptionArgument.item); + + try { + Insert(index, (T) item); + } + catch (InvalidCastException) { + ThrowHelper.ThrowWrongValueTypeArgumentException(item, typeof(T)); + } + } + + // Inserts the elements of the given collection at a given index. If + // required, the capacity of the list is increased to twice the previous + // capacity or the new size, whichever is larger. Ranges may be added + // to the end of the list by setting index to the List's size. + // + public void InsertRange(int index, IEnumerable<T> collection) { + if (collection==null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); + } + + if ((uint)index > (uint)_size) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); + } + Contract.EndContractBlock(); + + ICollection<T> c = collection as ICollection<T>; + if( c != null ) { // if collection is ICollection<T> + int count = c.Count; + if (count > 0) { + EnsureCapacity(_size + count); + if (index < _size) { + Array.Copy(_items, index, _items, index + count, _size - index); + } + + // If we're inserting a List into itself, we want to be able to deal with that. + if (this == c) { + // Copy first part of _items to insert location + Array.Copy(_items, 0, _items, index, index); + // Copy last part of _items back to inserted location + Array.Copy(_items, index+count, _items, index*2, _size-index); + } + else { + T[] itemsToInsert = new T[count]; + c.CopyTo(itemsToInsert, 0); + itemsToInsert.CopyTo(_items, index); + } + _size += count; + } + } + else { + using(IEnumerator<T> en = collection.GetEnumerator()) { + while(en.MoveNext()) { + Insert(index++, en.Current); + } + } + } + _version++; + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at the end + // and ending at the first element in the list. The elements of the list + // are compared to the given value using the Object.Equals method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item) + { + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(Contract.Result<int>() < Count); + if (_size == 0) { // Special case for empty list + return -1; + } + else { + return LastIndexOf(item, _size - 1, _size); + } + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at index + // index and ending at the first element in the list. The + // elements of the list are compared to the given value using the + // Object.Equals method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item, int index) + { + if (index >= _size) + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_Index); + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(((Count == 0) && (Contract.Result<int>() == -1)) || ((Count > 0) && (Contract.Result<int>() <= index))); + Contract.EndContractBlock(); + return LastIndexOf(item, index, index + 1); + } + + // Returns the index of the last occurrence of a given value in a range of + // this list. The list is searched backwards, starting at index + // index and upto count elements. The elements of + // the list are compared to the given value using the Object.Equals + // method. + // + // This method uses the Array.LastIndexOf method to perform the + // search. + // + public int LastIndexOf(T item, int index, int count) { + if ((Count != 0) && (index < 0)) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if ((Count !=0) && (count < 0)) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + Contract.Ensures(Contract.Result<int>() >= -1); + Contract.Ensures(((Count == 0) && (Contract.Result<int>() == -1)) || ((Count > 0) && (Contract.Result<int>() <= index))); + Contract.EndContractBlock(); + + if (_size == 0) { // Special case for empty list + return -1; + } + + if (index >= _size) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection); + } + + if (count > index + 1) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_BiggerThanCollection); + } + + return Array.LastIndexOf(_items, item, index, count); + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + // + public bool Remove(T item) { + int index = IndexOf(item); + if (index >= 0) { + RemoveAt(index); + return true; + } + + return false; + } + + void System.Collections.IList.Remove(Object item) + { + if(IsCompatibleObject(item)) { + Remove((T) item); + } + } + + // This method removes all items which matches the predicate. + // The complexity is O(n). + public int RemoveAll(Predicate<T> match) { + if( match == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + Contract.Ensures(Contract.Result<int>() >= 0); + Contract.Ensures(Contract.Result<int>() <= Contract.OldValue(Count)); + Contract.EndContractBlock(); + + int freeIndex = 0; // the first free slot in items array + + // Find the first item which needs to be removed. + while( freeIndex < _size && !match(_items[freeIndex])) freeIndex++; + if( freeIndex >= _size) return 0; + + int current = freeIndex + 1; + while( current < _size) { + // Find the first item which needs to be kept. + while( current < _size && match(_items[current])) current++; + + if( current < _size) { + // copy item to the free slot. + _items[freeIndex++] = _items[current++]; + } + } + + Array.Clear(_items, freeIndex, _size - freeIndex); + int result = _size - freeIndex; + _size = freeIndex; + _version++; + return result; + } + + // Removes the element at the given index. The size of the list is + // decreased by one. + // + public void RemoveAt(int index) { + if ((uint)index >= (uint)_size) { + ThrowHelper.ThrowArgumentOutOfRange_IndexException(); + } + Contract.EndContractBlock(); + _size--; + if (index < _size) { + Array.Copy(_items, index + 1, _items, index, _size - index); + } + _items[_size] = default(T); + _version++; + } + + // Removes a range of elements from this list. + // + public void RemoveRange(int index, int count) { + if (index < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (count < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + Contract.EndContractBlock(); + + if (count > 0) { + int i = _size; + _size -= count; + if (index < _size) { + Array.Copy(_items, index + count, _items, index, _size - index); + } + Array.Clear(_items, _size, count); + _version++; + } + } + + // Reverses the elements in this list. + public void Reverse() { + Reverse(0, Count); + } + + // Reverses the elements in a range of this list. Following a call to this + // method, an element in the range given by index and count + // which was previously located at index i will now be located at + // index index + (index + count - i - 1). + // + public void Reverse(int index, int count) { + if (index < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (count < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + Contract.EndContractBlock(); + + // The non-generic Array.Reverse is not used because it does not perform + // well for non-primitive value types. + // If/when a generic Array.Reverse<T> becomes available, the below code + // can be deleted and replaced with a call to Array.Reverse<T>. + int i = index; + int j = index + count - 1; + T[] array = _items; + while (i < j) + { + T temp = array[i]; + array[i] = array[j]; + array[j] = temp; + i++; + j--; + } + + _version++; + } + + // Sorts the elements in this list. Uses the default comparer and + // Array.Sort. + public void Sort() + { + Sort(0, Count, null); + } + + // Sorts the elements in this list. Uses Array.Sort with the + // provided comparer. + public void Sort(IComparer<T> comparer) + { + Sort(0, Count, comparer); + } + + // Sorts the elements in a section of this list. The sort compares the + // elements to each other using the given IComparer interface. If + // comparer is null, the elements are compared to each other using + // the IComparable interface, which in that case must be implemented by all + // elements of the list. + // + // This method uses the Array.Sort method to sort the elements. + // + public void Sort(int index, int count, IComparer<T> comparer) { + if (index < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (count < 0) { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum); + } + + if (_size - index < count) + ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); + Contract.EndContractBlock(); + + Array.Sort<T>(_items, index, count, comparer); + _version++; + } + + public void Sort(Comparison<T> comparison) { + if( comparison == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison); + } + Contract.EndContractBlock(); + + if( _size > 0) { + IComparer<T> comparer = Comparer<T>.Create(comparison); + Array.Sort(_items, 0, _size, comparer); + } + } + + // ToArray returns an array containing the contents of the List. + // This requires copying the List, which is an O(n) operation. + public T[] ToArray() { + Contract.Ensures(Contract.Result<T[]>() != null); + Contract.Ensures(Contract.Result<T[]>().Length == Count); + +#if FEATURE_CORECLR + if (_size == 0) + { + return _emptyArray; + } +#endif + + T[] array = new T[_size]; + Array.Copy(_items, 0, array, 0, _size); + return array; + } + + // Sets the capacity of this list to the size of the list. This method can + // be used to minimize a list's memory overhead once it is known that no + // new elements will be added to the list. To completely clear a list and + // release all memory referenced by the list, execute the following + // statements: + // + // list.Clear(); + // list.TrimExcess(); + // + public void TrimExcess() { + int threshold = (int)(((double)_items.Length) * 0.9); + if( _size < threshold ) { + Capacity = _size; + } + } + + public bool TrueForAll(Predicate<T> match) { + if( match == null) { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); + } + Contract.EndContractBlock(); + + for(int i = 0 ; i < _size; i++) { + if( !match(_items[i])) { + return false; + } + } + return true; + } + + [Serializable] + public struct Enumerator : IEnumerator<T>, System.Collections.IEnumerator + { + private List<T> list; + private int index; + private int version; + private T current; + + internal Enumerator(List<T> list) { + this.list = list; + index = 0; + version = list._version; + current = default(T); + } + + public void Dispose() { + } + + public bool MoveNext() { + + List<T> localList = list; + + if (version == localList._version && ((uint)index < (uint)localList._size)) + { + current = localList._items[index]; + index++; + return true; + } + return MoveNextRare(); + } + + private bool MoveNextRare() + { + if (version != list._version) { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + index = list._size + 1; + current = default(T); + return false; + } + + public T Current { + get { + return current; + } + } + + Object System.Collections.IEnumerator.Current { + get { + if( index == 0 || index == list._size + 1) { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); + } + return Current; + } + } + + void System.Collections.IEnumerator.Reset() { + if (version != list._version) { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); + } + + index = 0; + current = default(T); + } + + } + } +} + |