. Net source code analysis-List & lt; T & gt;,. netlist

Source: Internet
Author: User

. Net source code analysis-List <T>,. netlist

By analyzing the source code, you can better understand the working method of List <T> and help us write more stable code.

List <T> Source Code address: Sources.

Interface

List <T> Implemented interfaces: IList <T>, IList, IReadOnlyList <T>

In fact, after multi-generation development of. net framework, there are indeed a lot of List interfaces. To be compatible with old functions when adding new functions, some old interfaces cannot be lost, so it looks a little complicated. First, repeat these interfaces:

IEnumerator is an enumerator interface that provides enumeration element functions. Its members include Current, MoveNext, and Reset. These three functions allow the collection to support traversal.

IEnumerable supports the enumeration interface, which means that traversal is supported and the member is the IEnumerator above.

ICollection is a collection interface that supports the Count attribute and CopyTo operation of the set. In addition, it also includes the synchronous attribute IsSynchronized (to determine whether the thread is secure) and SyncRoot (lock Object ).

IList is a set operation interface that supports operations such as indexer, Add, Remove, Insert, and Contains.

The generic part is basically the generic implementation of the above interfaces, but some IList <T> operations are placed in ICollection <T>, microsoft may also think it is more reasonable to put some operations on the collection into ICollection.

IReadOnlyCollection <T> is added to. net 4.5 and can be considered as a read-only version of IList <T>.

Variable
 1 private const int _defaultCapacity = 4; 2  3 private T[] _items; 4  5 private int _size; 6  7 private int _version; 8  9 private Object _syncRoot;10 11 static readonly T[] _emptyArray = new T[0];

_ Defacapcapacity indicates that the default size of the new List <T> is 4.

_ Items is an array of elements stored in List <T>, and List <T> is also implemented based on arrays.

_ Size indicates the number of elements.

_ Version indicates a version. The specific usage is as follows, which is related to the collection modification exceptions that are frequently encountered during collection traversal.

_ SyncRoot the built-in lock objects are mentioned above. If you only operate this set in multiple threads, you can lock this object to ensure thread security. Of course, this is generally used internally, although it is useless for List <T> itself, if this is not obtained, the object will not be new. For locking, we are more often using a new readonly object outside.

EmptyArray this is a static read-only empty array. All lists without elements <T> use this array. Therefore, the _ items of the two lists <int> are actually the same, all of which are the _ emptyArray.

Constructor

There are three Constructors

1 public List()2 {3     _items = _emptyArray;4 }

Most commonly used, _ items points directly to a static empty array.

 1 public List(int capacity) 2 { 3     if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), capacity, SR.ArgumentOutOfRange_NeedNonNegNum); 4     Contract.EndContractBlock(); 5  6     if (capacity == 0) 7         _items = _emptyArray; 8     else 9         _items = new T[capacity];10 }

You can specify the size through capacity.

 1 public List(IEnumerable<T> collection) 2 { 3     if (collection == null) 4         throw new ArgumentNullException(nameof(collection)); 5     Contract.EndContractBlock(); 6  7     ICollection<T> c = collection as ICollection<T>; 8     if (c != null) 9     {10         int count = c.Count;11         if (count == 0)12         {13             _items = _emptyArray;14         }15         else16         {17             _items = new T[count];18             c.CopyTo(_items, 0);19             _size = count;20         }21     }22     else23     {24         _size = 0;25         _items = _emptyArray;26         // This enumerable could be empty.  Let Add allocate a new array, if needed.27         // Note it will also go to _defaultCapacity first, not 1, then 2, etc.28 29         using (IEnumerator<T> en = collection.GetEnumerator())30         {31             while (en.MoveNext())32             {33                 Add(en.Current);34             }35         }36     }37 }

Add a collection first. check whether it is an ICollection. check whether this interface has the Copy function and copy it to _ items. If it is not ICollection, but because it is IEnumerable, You can traverse it and add it to _ items one by one.

Attribute

Count Returns _ size, which is the actual number of elements, not the array size.

IsSynchronized is false, indicating that SyncRoot is not used for synchronization. List <T> is not thread-safe. We need to use the lock to fix it ourselves,

IsReadOnly is also false. Why should I inherit IReadOnlyList <T> to provide an opportunity to convert it to a read-only List? For example, some methods do not want to modify the List to be passed in, you can set the parameter to IReadOnlyList.

 1 Object System.Collections.ICollection.SyncRoot 2 { 3     get 4     { 5         if (_syncRoot == null) 6         { 7             System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null); 8         } 9         return _syncRoot;10     }11 }

SyncRoot obtains an object through atomic operations, which is useless for List <T> and useful for some sets. For example, SyncHashtable is used to implement thread security through syncRoot.

Important Capacity:

 1 public int Capacity 2 { 3     get 4     { 5         Contract.Ensures(Contract.Result<int>() >= 0); 6         return _items.Length; 7     } 8     set 9     {10         if (value < _size)11         {12             throw new ArgumentOutOfRangeException(nameof(value), value, SR.ArgumentOutOfRange_SmallCapacity);13         }14         Contract.EndContractBlock();15 16         if (value != _items.Length)17         {18             if (value > 0)19             {20                 var items = new T[value];21                 Array.Copy(_items, 0, items, 0, _size);22                 _items = items;23             }24             else25             {26                 _items = _emptyArray;27             }28         }29     }30 }

Capacity is the length of the Array. In addition, we can use Capacity to set the size of the List. Even if an element already exists in the List, an Array of the target size will be added first, and then the Array will be used. copy Copies existing elements to the new array. However, we do not need to set Capacity. When adding new elements, we find that the length is not enough and the array is automatically expanded. Capacity is int type, indicating the maximum value is int. maxValue, about 2 GB. If we directly set int for List. maxValue depends on whether your memory is 2 GB x 4 or 8 GB. If not, an OutofMemory Exception is reported. In fact, I personally think it is better to use uint in Capacity.

MB memory, more than MB

The memory size is also mb. Because it is long, the memory usage is more than MB.

Method

Let's look at several important methods:

1 public void Add(T item)2 {3     if (_size == _items.Length) EnsureCapacity(_size + 1);4     _items[_size++] = item;5     _version++;6 }

When the size of the current array is equal to the number of elements, it indicates that the size is not enough if you Add the array. You must first use EnsureCapacity to scale up. _ size + 1 indicates a minimum scale-up target.

 1 private void EnsureCapacity(int min) 2 { 3     if (_items.Length < min) 4     { 5         int newCapacity = _items.Length == 0 ? _defaultCapacity : _items.Length * 2; 6         // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow. 7         // Note that this check works even when _items.Length overflowed thanks to the (uint) cast 8         //if ((uint)newCapacity > Array.MaxArrayLength) newCapacity = Array.MaxArrayLength; 9         if (newCapacity < min) newCapacity = min;10         Capacity = newCapacity;11     }12 }

Expansion method. If the array length is 0, _ defacapcapacity, that is, 4, is used as the array length. Otherwise, it is expanded by twice the number of current elements. If the new length is smaller than the input min, use min, that is, select a large value. This situation may occur in InsertRange, because the insert list may have more elements than the current list.

The Add function also contains _ version ++, which can be seen in many methods, such as remove, insert, and sort. If you want to modify a set, you need _ version ++. What is the use of this _ version?

 1 public void ForEach(Action<T> action) 2 { 3     if (action == null) 4     { 5         throw new ArgumentNullException(nameof(action)); 6     } 7  8     int version = _version; 9 10     for (int i = 0; i < _size; i++)11     {12         if (version != _version)13         {14             break;15         }16         action(_items[i]);17     }18 19     if (version != _version)20         throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion);21 }

If _ version changes during the traversal process, the system immediately exits and throws an exception in modifying the traversal process set. For example, removing or adding the element in foreach causes this exception. More often, when one thread traverses the set during multithreading and the other thread modifies the set, it is believed that many people have suffered hardships.

If a thread wants to modify the set during the time period, such as deleting the set, you can use the original for (int I = list. Count-1; I> = 0; I --) method.

In addition, the version also has the Enumerator, which will be detected during the MoveNext process.

Most of the other methods are implemented through static functions of Array. Not to mention, you need to note that List <T> inherits from IList, so it can be converted to IList. After conversion, the generics will be gone, if it is a List <int>, converting it to IList is no different from IList <object>. The performance loss caused by binning is also worth noting.

Summary

List <T> the initial size is 4. automatic resizing is based on twice the number of elements in the current array element or the number of elements in the InsertRange target list (which election is used ). If the size is determined, you can set it in advance, because the array and copy elements need to be re-allocated for each automatic resizing, and the performance loss is not small.

List <T> uses version to track whether the set has changed. If the foreach duration changes, an exception is thrown.

List <T> is not thread-safe. You must consider whether multithreading exists in the current environment and whether to use a lock to ensure the thread security of the set.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.