C# 中的 數組[]、ArrayList、List


C# 中的 數組[]ArrayListList

數組

在 C# 中,數組實際上是對象,而不只是如在 C 和 C++ 中的連續內存的可尋址區域。

屬性:

  • 數組可以是一維、多維或交錯的。
  • 創建數組實例時,將建立緯度數量和每個緯度的長度。 這些值在實例的生存期內無法更改。
  • 數值數組元素的默認值設置為零,而引用元素設置為 null。
  • 交錯數組是數組的數組,因此其元素為引用類型且被初始化為 null。
  • 數組從零開始編制索引:包含 n 元素的數組從 0 索引到 n-1。
  • 數組元素可以是任何類型,其中包括數組類型。
  • 數組類型是從抽象的基類型 Array 派生的引用類型。 所有數組都會實現 IListIEnumerable。 可在 C# 中使用 foreach 迭代數組。 原因是單維數組還實現了 IList<T>IEnumerable<T>
  1. 命名空間: System;

  2. 特點
    內存連續存儲;索引速度快;賦值修改元素簡單

  3. 缺點
    插入數據麻煩(連續內存,插入后續的元素都需要移動);聲明數組需指定長度(長了沒用完浪費內存,短了可能不夠用);

  4. 分類

    • 單維數組 eg: int[] array = new int[5];
    • 多維數組 eg:
    int[,] array = new int[4, 2]; // 四行兩列的二維數組
    int[,,] array3Da = new int[2, 2, 3] { 
        { { 1, 2, 3 }, {  4,  5,  6 } },
        { { 7, 8, 9 }, { 10, 11, 12 } }
    }; // 三個維度(2、2 和 3)的數組
    
    • 交錯數組 eg:
    // 聲明一個具有三個元素的一維數組,其中每個元素都是一維整數數組
    int[][] jaggedArray = new int[3][];
    
    // 必須初始化的元素后才可使用它
    jaggedArray[0] = new int[5];
    jaggedArray[1] = new int[4];
    jaggedArray[2] = new int[2];
    
    • 隱式類型的數組
      通常用於查詢表達式、匿名類型、對象和集合初始值設定項.
    var a = new[] { 1, 10, 100, 1000 }; // int[]
    var b = new[] { "hello", null, "world" }; // string[]
    

Array

數組類型([])是從抽象的基類型 Array 派生的引用類型。

Array 類提供一些方法,用於創建、處理、搜索數組並對數組進行排序,從而充當公共語言運行時中所有數組的基類。

Array的用法與數組[]幾乎一樣,可以看做是數組。在定義的時候需要指定長度。

Array 的 公共靜態成員(public static)是線程安全的。但不保證所有實例成員都是線程安全的。

Array.SyncRoot 屬性,用於同步對 Array 的訪問的對象。

下面的代碼示例演示如何使用屬性在整個枚舉過程中鎖定數組 SyncRoot

Array myArray = new int[] { 1, 2, 4 };
lock(myArray.SyncRoot)
{
    foreach (Object item in myArray)
        Console.WriteLine(item);
}

ArrayList

為了解決數組的一些短板,ArrayList 繼承了 IList 接口,提供了數據存儲和檢索。 ArrayList 對象的大小是按照其中存儲的數據來動態擴充與收縮的。在聲明 ArrayList 對象時並不需要指定它的長度。

  1. 命名空間: System.Collections;
  2. 特點
    允許插入不同類型的數據(插入object),無需指定長度;只有一個維度
  3. 缺點
    處理數據可能會報類型不匹配的錯誤;在存儲或檢索值類型時通常發生裝箱和取消裝箱操作,性能耗損較大

裝箱:就是將值類型的數據打包到引用類型的實例中
拆箱:就是從引用數據中提取值類型

一些方法:
public virtual int Add(object? value);:將對象添加到 ArrayList 的結尾處,返回已添加 valueArrayList 索引

public virtual void Remove (object? obj);:從 ArrayList 中移除特定對象的第一個匹配

ArrayList 是使用 object 數組 實現的,它涉及拆箱和裝箱。默認容量 4

public class ArrayList : IList, ICloneable
{
    private object?[] _items; // Do not rename (binary serialization)
    private int _size; // Do not rename (binary serialization)
    private int _version; // Do not rename (binary serialization)

    private const int _defaultCapacity = 4;

    // Constructs a ArrayList. 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 ArrayList()
    {
        _items = Array.Empty<object>();
    }

    // Constructs a ArrayList 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 ArrayList(int capacity)
    {
        if (capacity < 0) throw new ArgumentOutOfRangeException(nameof(capacity), SR.Format(SR.ArgumentOutOfRange_MustBeNonNegNum, nameof(capacity)));

        if (capacity == 0)
            _items = Array.Empty<object>();
        else
            _items = new object[capacity];
    }
}

擴容:
ArrayList 在內部有一個存放數據的數組,當新增數據時候,如果該數組有可用,則會將數據放入數組,並將下標向后移動,如果沒有足夠的數組,則會進行擴容,如果創建的時候沒有給定容量,第一次擴容則會使用默認的容量,如果當前有元素,則會擴容至當前容量的兩倍。
可以看到擴容 是將原數組的數據拷貝到新創建的數組中

// 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 virtual int Add(object? value)
{
    if (_size == _items.Length) EnsureCapacity(_size + 1);
    _items[_size] = value;
    _version++;
    return _size++;
}

// Ensures that the capacity of this list is at least the given minimum
// value. If the current 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.MaxLength) newCapacity = Array.MaxLength;
        if (newCapacity < min) newCapacity = min;
        Capacity = newCapacity;
    }
}

// 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 virtual int Capacity
{
    get => _items.Length;
    set
    {
        if (value < _size)
        {
            throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_SmallCapacity);
        }

        // We don't want to update the version number when we change the capacity.
        // Some existing applications have dependency on this.
        if (value != _items.Length)
        {
            if (value > 0)
            {
                object[] newItems = new object[value];
                if (_size > 0)
                {
                    Array.Copy(_items, newItems, _size);
                }
                _items = newItems;
            }
            else
            {
                _items = new object[_defaultCapacity];
            }
        }
    }
}

ArrayList源碼

List

通過使用大小根據需要動態增加的數組來實現泛型接口。

相比於ArrayListList<T>不存在裝箱拆箱的缺點,List類是ArrayList類的泛型等效類,它的大部分用法都與ArrayList相似,因為List類也繼承了IList接口。最關鍵的區別在於,在聲明List集合時,需要為其聲明List集合內數據的對象類型。

  1. 命名空間 System.Collections.Generic
  2. 特點
    插入類型固定(泛型);無需指定長度,只有一個維度,允許重復元素

List底層實現使用 泛型數組(Array),默認容量 4,初始化時候可以指定初始化容量,如果不指定則會給定一個空的泛型數組。

public class List<T>:IList<T>,IList,IReadOnlyList<T>
{
    private const int DefaultCapacity = 4;
    internal T[] _items;
    internal int _size;
    private int _version;

    public List()
    {
        _items = s_emptyArray;
    }

    public List(int capacity)
    {
        if (capacity < 0)
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);

        if (capacity == 0)
            _items = s_emptyArray;
        else
            _items = new T[capacity];
    }
}

擴容: List 在內部有一個存放數據的數組,當新增數據時候,如果該數組有可用,則會將數據放入數組,並將下標向后移動,如果沒有足夠的數組,則會進行擴容,如果創建的時候沒有給定容量,第一次擴容則會使用默認的容量,如果當前有元素,則會擴容至當前容量的兩倍。
可以看到擴容 是將原數組的數據拷貝到新創建的數組中

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Add(T item)
{
    _version++;
    T[] array = _items;
    int size = _size;
    if ((uint)size < (uint)array.Length)
    {
        _size = size + 1;
        array[size] = item;
    }
    else
    {
        AddWithResize(item);
    }
}
 
// Non-inline from List.Add to improve its code quality as uncommon path
[MethodImpl(MethodImplOptions.NoInlining)]
private void AddWithResize(T item)
{
    Debug.Assert(_size == _items.Length);
    int size = _size;
    Grow(size + 1);  // 擴容
    _size = size + 1;
    _items[size] = item;
}

private void Grow(int capacity)
{
    Debug.Assert(_items.Length < capacity);

    int newcapacity = _items.Length == 0 ? DefaultCapacity : 2 * _items.Length;

    // 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.MaxLength) newcapacity = Array.MaxLength;

    // If the computed capacity is still less than specified, set to the original argument.
    // Capacities exceeding Array.MaxLength will be surfaced as OutOfMemoryException by Array.Resize.
    if (newcapacity < capacity) newcapacity = capacity;

    Capacity = newcapacity;
}

// 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 => _items.Length;
    set
    {
        if (value < _size)
        {
            ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value, ExceptionResource.ArgumentOutOfRange_SmallCapacity);
        }

        if (value != _items.Length)
        {
            if (value > 0)
            {
                T[] newItems = new T[value];
                if (_size > 0)
                {
                    Array.Copy(_items, newItems, _size);
                }
                _items = newItems;
            }
            else
            {
                _items = s_emptyArray;
            }
        }
    }
}
 

List 源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM