基本数据结构解析之ArrayList

ArrayList:

使用大小可按需动态增加的数组实现 IList 接口。(From MSDN)

初始化, 内部默认是4的Capacity, 还是采用Object存储

   1: private const int _defaultCapacity = 4;
   2: private Object[] _items;
   3: private static readonly Object[] emptyArray = new Object[0]; 
   4:  
   5: // Note: this constructor is a bogus constructor that does nothing
   6: // and is for use only with SyncArrayList.
   7: internal ArrayList( bool trash )
   8: {
   9: }
  10:  
  11: // Constructs a ArrayList. The list is initially empty and has a capacity
  12: // of zero. Upon adding the first element to the list the capacity is
  13: // increased to _defaultCapacity, and then increased in multiples of two as required.
  14: public ArrayList() {
  15:     _items = emptyArray;  
  16: }
  17:  
  18: // Constructs a ArrayList with a given initial capacity. The list is
  19: // initially empty, but will have room for the given number of elements
  20: // before any reallocations are required.
  21: // 
  22:  public ArrayList(int capacity) {
  23:     if (capacity < 0) throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum", "capacity"));
  24:     _items = new Object[capacity];
  25: }
  26:  
  27: // Constructs a ArrayList, copying the contents of the given collection. The
  28: // size and capacity of the new list will both be equal to the size of the
  29: // given collection.
  30: // 
  31: public ArrayList(ICollection c) {
  32:     if (c==null)
  33:         throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
  34:     _items = new Object[c.Count];
  35:     AddRange(c);
  36: }

Capacity 比较有趣的是如果设置新的Capacity, 如果大于原有的length则会发生

1. 建立新的以新的Capacity为容量大小的Object数组

2. 将原有数据拷贝到新的数组上去

3. 将原有引用指向新的Object数组上去

   1: // Gets and sets the capacity of this list.  The capacity is the size of
   2: // the internal array used to hold items.  When set, the internal 
   3: // array of the list is reallocated to the given capacity.
   4: // 
   5:  public virtual int Capacity {
   6:     get { return _items.Length; }
   7:     set {
   8:         // We don't want to update the version number when we change the capacity.
   9:         // Some existing applications have dependency on this.
  10:         if (value != _items.Length) {
  11:             if (value < _size) {
  12:                 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
  13:             }
  14:  
  15:             if (value > 0) {
  16:                 Object[] newItems = new Object[value];
  17:                 if (_size > 0) { 
  18:                     Array.Copy(_items, 0, newItems, 0, _size);
  19:                 }
  20:                 _items = newItems;
  21:             }
  22:             else {
  23:                 _items = new Object[_defaultCapacity];
  24:             }
  25:         }            
  26:     }

ListWrapper 把一个IList转换为ArrayList

   1: // Creates a ArrayList wrapper for a particular IList.  This does not
   2: // copy the contents of the IList, but only wraps the ILIst.  So any
   3: // changes to the underlying list will affect the ArrayList.  This would
   4: // be useful if you want to Reverse a subrange of an IList, or want to
   5: // use a generic BinarySearch or Sort method without implementing one yourself.
   6: // However, since these methods are generic, the performance may not be
   7: // nearly as good for some operations as they would be on the IList itself.
   8: //
   9: public static ArrayList Adapter(IList list) {
  10:     if (list==null)
  11:         throw new ArgumentNullException("list");
  12:     return new IListWrapper(list);
  13: }

添加元素, 在超出容量时还是会Double原来的Size

   1: // Adds the given object to the end of this list. The size of the list is
   2: // increased by one. If required, the capacity of the list is doubled
   3: // before adding the new element.
   4: //
   5: public virtual int Add(Object value) {
   6:     if (_size == _items.Length) EnsureCapacity(_size + 1);
   7:     _items[_size] = value;
   8:     _version++;
   9:     return _size++;
  10: }
  11:  
  12: // Ensures that the capacity of this list is at least the given minimum
  13: // value. If the currect capacity of the list is less than min, the
  14: // capacity is increased to twice the current capacity or to min,
  15: // whichever is larger.
  16: private void EnsureCapacity(int min) {
  17:     if (_items.Length < min) {
  18:         int newCapacity = _items.Length == 0? _defaultCapacity: _items.Length * 2;
  19:         if (newCapacity < min) newCapacity = min;
  20:         Capacity = newCapacity;
  21:     }
  22: }

批量插入元素, 和插入元素类似

   1: // Adds the elements of the given collection to the end of this list. If
   2: // required, the capacity of the list is increased to twice the previous
   3: // capacity or the new size, whichever is larger.
   4: //
   5: public virtual void AddRange(ICollection c) {
   6:     InsertRange(_size, c);
   7: }
   8:  
   9: // Inserts the elements of the given collection at a given index. If
  10: // required, the capacity of the list is increased to twice the previous
  11: // capacity or the new size, whichever is larger.  Ranges may be added
  12: // to the end of the list by setting index to the ArrayList's size.
  13: //
  14: public virtual void InsertRange(int index, ICollection c) {
  15:     if (c==null)
  16:         throw new ArgumentNullException("c", Environment.GetResourceString("ArgumentNull_Collection"));
  17:     if (index < 0 || index > _size) throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  18:     int count = c.Count;
  19:     if (count > 0) {
  20:         EnsureCapacity(_size + count);                
  21:         // shift existing items
  22:         if (index < _size) {
  23:             Array.Copy(_items, index, _items, index + count, _size - index);
  24:         }
  25:  
  26:         Object[] itemsToInsert = new Object[count];
  27:         c.CopyTo(itemsToInsert, 0);
  28:         itemsToInsert.CopyTo(_items, index);
  29:         _size += count;
  30:         _version++;
  31:     }
  32: }

二分查找

   1: // Searches a section of the list for a given element using a binary search
   2:   // algorithm. Elements of the list are compared to the search value using
   3:   // the given IComparer interface. If comparer is null, elements of
   4:   // the list are compared to the search value using the IComparable
   5:   // interface, which in that case must be implemented by all elements of the
   6:   // list and the given search value. This method assumes that the given
   7:   // section of the list is already sorted; if this is not the case, the
   8:   // result will be incorrect.
   9:   //
  10:   // The method returns the index of the given value in the list. If the
  11:   // list does not contain the given value, the method returns a negative
  12:   // integer. The bitwise complement operator (~) can be applied to a
  13:   // negative result to produce the index of the first element (if any) that
  14:   // is larger than the given search value. This is also the index at which
  15:   // the search value should be inserted into the list in order for the list
  16:   // to remain sorted.
  17:   // 
  18:   // The method uses the Array.BinarySearch method to perform the
  19:   // search.
  20:   // 
  21:   public virtual int BinarySearch(int index, int count, Object value, IComparer comparer) {
  22:       if (index < 0 || count < 0)
  23:           throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  24:       if (_size - index < count)
  25:           throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  26:  
  27:       return Array.BinarySearch((Array)_items, index, count, value, comparer);
  28:   }
  29:  
  30:   public virtual int BinarySearch(Object value)
  31:   {
  32:       return BinarySearch(0,Count,value,null);
  33:   }
  34:  
  35:   public virtual int BinarySearch(Object value, IComparer comparer)
  36:   {
  37:       return BinarySearch(0,Count,value,comparer);
  38:   }

调用Array.BinarySearch, 关于BinarySearch的内容较多,您可以查看MSDN, 还有这里看源码

不知道二分查找?看这里

这里的原理: 如果有Comparer则调用Comparer进行二分查找.如果是Comparer.Default在运行时查看类型,采用Switch进行处理,调用对应类型ArrayHelper<KIND>.BinarySearchBitwiseEquals,

   1: // Searches a section of an array for a given element using a binary search
   2: // algorithm. Elements of the array are compared to the search value using
   3: // the IComparable interface, which must be implemented by all
   4: // elements of the array and the given search value. This method assumes
   5: // that the array is already sorted according to the IComparable
   6: // interface; if this is not the case, the result will be incorrect.
   7: //
   8: // The method returns the index of the given value in the array. If the
   9: // array does not contain the given value, the method returns a negative
  10: // integer. The bitwise complement operator (~) can be applied to a
  11: // negative result to produce the index of the first element (if any) that
  12: // is larger than the given search value.
  13: // 
  14: [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  15: public static int BinarySearch(Array array, int index, int length, Object value) {
  16:    return BinarySearch(array, index, length, value, null);
  17: }
  18:  
  19: // Searches an array for a given element using a binary search algorithm.
  20: // Elements of the array are compared to the search value using the given
  21: // IComparer interface. If comparer is null, elements of the
  22: // array are compared to the search value using the IComparable
  23: // interface, which in that case must be implemented by all elements of the
  24: // array and the given search value. This method assumes that the array is
  25: // already sorted; if this is not the case, the result will be incorrect.
  26: // 
  27: // The method returns the index of the given value in the array. If the
  28: // array does not contain the given value, the method returns a negative
  29: // integer. The bitwise complement operator (~) can be applied to a
  30: // negative result to produce the index of the first element (if any) that
  31: // is larger than the given search value.
  32: // 
  33: [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  34: public static int BinarySearch(Array array, Object value, IComparer comparer) {
  35:    if (array==null)
  36:        throw new ArgumentNullException("array");
  37:    int lb = array.GetLowerBound(0);
  38:    return BinarySearch(array, lb, array.Length, value, comparer);
  39: }
  40:  
  41: // Searches a section of an array for a given element using a binary search
  42: // algorithm. Elements of the array are compared to the search value using
  43: // the given IComparer interface. If comparer is null,
  44: // elements of the array are compared to the search value using the
  45: // IComparable interface, which in that case must be implemented by
  46: // all elements of the array and the given search value. This method
  47: // assumes that the array is already sorted; if this is not the case, the
  48: // result will be incorrect.
  49: // 
  50: // The method returns the index of the given value in the array. If the
  51: // array does not contain the given value, the method returns a negative
  52: // integer. The bitwise complement operator (~) can be applied to a
  53: // negative result to produce the index of the first element (if any) that
  54: // is larger than the given search value.
  55: // 
  56: [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  57: public static int BinarySearch(Array array, int index, int length, Object value, IComparer comparer) {
  58:    if (array==null) 
  59:        throw new ArgumentNullException("array");
  60:    int lb = array.GetLowerBound(0);
  61:    if (index < lb || length < 0)
  62:        throw new ArgumentOutOfRangeException((index<lb ? "index" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  63:    if (array.Length - (index - lb) < length)
  64:        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  65:    if (array.Rank != 1)
  66:        throw new RankException(Environment.GetResourceString("Rank_MultiDimNotSupported"));
  67:    
  68:    if (comparer == null) comparer = Comparer.Default;
  69:    if (comparer == Comparer.Default) {
  70:        int retval;
  71:        bool r = TrySZBinarySearch(array, index, length, value, out retval);
  72:        if (r)
  73:            return retval;
  74:    }
  75:  
  76:    int lo = index;
  77:    int hi = index + length - 1;   
  78:    Object[] objArray = array as Object[];
  79:    if(objArray != null) {
  80:        while (lo <= hi) {
  81:            // i might overflow if lo and hi are both large positive numbers. 
  82:            int i = GetMedian(lo, hi);
  83:  
  84:            int c;
  85:            try {
  86:                c = comparer.Compare(objArray[i], value);
  87:            }
  88:            catch (Exception e) {
  89:                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
  90:            }
  91:            catch {
  92:                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"));
  93:            }
  94:            if (c == 0) return i;
  95:            if (c < 0) {
  96:                lo = i + 1;
  97:            }
  98:            else {
  99:                hi = i - 1;
 100:            }
 101:        }
 102:    }
 103:    else {
 104:        while (lo <= hi) {
 105:            int i = GetMedian(lo, hi);                    
 106:  
 107:            int c;
 108:            try {
 109:                c = comparer.Compare(array.GetValue(i), value);
 110:            }
 111:            catch (Exception e) {
 112:                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), e);
 113:            }
 114:            catch {
 115:                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"));
 116:            }
 117:            if (c == 0) return i;
 118:            if (c < 0) {
 119:                lo = i + 1;
 120:            }
 121:            else {
 122:                hi = i - 1;
 123:            }
 124:        }
 125:    }
 126:    return ~lo;
 127: }

Clear, Clone, Contains 同上

CopyTo, 跟Clone的方式类似,不同的是增加了索引拷贝的方式

锁定数组, 可以对IList和ArrayList进行锁定,添加和删除操作都会失败,但是替换操作依然可以执行, 是这样的吗?

   1: // Returns a list wrapper that is fixed at the current size.  Operations
   2: // that add or remove items will fail, however, replacing items is allowed.
   3: //
   4: public static IList FixedSize(IList list) {
   5:    if (list==null)
   6:        throw new ArgumentNullException("list");
   7:    return new FixedSizeList(list);
   8: }
   9:  
  10: // Returns a list wrapper that is fixed at the current size.  Operations
  11: // that add or remove items will fail, however, replacing items is allowed.
  12: //
  13: public static ArrayList FixedSize(ArrayList list) {
  14:    if (list==null)
  15:        throw new ArgumentNullException("list");
  16:    return new FixedSizeArrayList(list);
  17: }

Reverse

首先会调用mscorworks中的方法来对对应的类型利用泛型的方式进行翻转。如果找不到对应的类型(即内建的基本类型)。则按首尾交换的原则进行翻转

调用方式如下查看代码

   1: // Reverses the elements in this list.
   2: public virtual void Reverse() {
   3:     Reverse(0, Count);
   4: }
   5:  
   6: // Reverses the elements in a range of this list. Following a call to this
   7: // method, an element in the range given by index and count
   8: // which was previously located at index i will now be located at
   9: // index index + (index + count - i - 1).
  10: // 
  11: // This method uses the Array.Reverse method to reverse the
  12: // elements.
  13: // 
  14: public virtual void Reverse(int index, int count) {
  15:     if (index < 0 || count < 0)
  16:         throw new ArgumentOutOfRangeException((index<0 ? "index" : "count"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  17:     if (_size - index < count)
  18:         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  19:     Array.Reverse(_items, index, count);
  20:     _version++;
  21: }
  22:  
  23: // Reverses the elements in a range of an array. Following a call to this
  24: // method, an element in the range given by index and count
  25: // which was previously located at index i will now be located at
  26: // index index + (index + count - i - 1).
  27: // Reliability note: This may fail because it may have to box objects.
  28: // 
  29: [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
  30: public static void Reverse(Array array, int index, int length) {
  31:     if (array==null) 
  32:         throw new ArgumentNullException("array");
  33:     if (index < array.GetLowerBound(0) || length < 0)
  34:         throw new ArgumentOutOfRangeException((index<0 ? "index" : "length"), Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  35:     if (array.Length - (index - array.GetLowerBound(0)) < length)
  36:         throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
  37:     if (array.Rank != 1)
  38:         throw new RankException(Environment.GetResourceString("Rank_MultiDimNotSupported"));
  39:  
  40:     bool r = TrySZReverse(array, index, length);
  41:     if (r)
  42:         return;
  43:  
  44:     int i = index;
  45:     int j = index + length - 1;
  46:     Object[] objArray = array as Object[];
  47:     if (objArray!=null) {
  48:         while (i < j) {
  49:             Object temp = objArray[i];
  50:             objArray[i] = objArray[j];
  51:             objArray[j] = temp;
  52:             i++;
  53:             j--;
  54:         }
  55:     }
  56:     else {
  57:         while (i < j) {
  58:             Object temp = array.GetValue(i);
  59:             array.SetValue(array.GetValue(j), i);
  60:             array.SetValue(temp, j);
  61:             i++;
  62:             j--;
  63:         }
  64:     }
  65: }
  66: static void Reverse(KIND array[], UINT32 index, UINT32 count) {
  67:     LEAF_CONTRACT;
  68:  
  69:     _ASSERTE(array != NULL);
  70:     if (count == 0) {
  71:         return;
  72:     }
  73:     UINT32 i = index;
  74:     UINT32 j = index + count - 1;
  75:     while(i < j) {
  76:         KIND temp = array[i];
  77:         array[i] = array[j];
  78:         array[j] = temp;
  79:         i++;
  80:         j--;
  81:     }
  82: }

SetValue

通过读取index的索引处的指针, 调用内部的实现来设置值

   1: InternalGetReference(&elemref, 3, pIndices);
   2: InternalSetValue(&elemref,value);
   3: [MethodImplAttribute(MethodImplOptions.InternalCall)]
   4: // reference to TypedReference is banned, so have to pass result as pointer
   5: private unsafe extern void InternalGetReference(void * elemRef, int rank, int * pIndices);
   6:  
   7: [MethodImplAttribute(MethodImplOptions.InternalCall)]
   8: private unsafe extern static void InternalSetValue(void * target, Object value);

Sort

首先查找内部底层实现中对应类型的快速排序,如果没有,则在BCL层次利用ICompare接口实现进行快速排序

代码太大,查看

CLR/SRC/SYSTEM/ArrayList.cs, Array.cs

CLR/SRC/VM/Array.cpp, ComArrayHelper.cpp

posted on 2008-11-29 19:01  xwang  阅读(1529)  评论(1编辑  收藏  举报

导航