程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET,你忘記了麼?(三續)——重新理解List<T>

.NET,你忘記了麼?(三續)——重新理解List<T>

編輯:關於.NET

在上文中,《.NET,你忘記了麼(三)——關於Array和List的使用》中,我對 List<T>的理解大錯特錯,在成文前,首先做下自我批評,然後也對造成的不良影響表 示道歉。

周四面試的慘敗,讓我的心情著實糟糕了兩三天。痛定思痛,決心回家繼續苦讀。

首先開始的就是對List的重新認知。在這裡,讓我們先從構造方法來重新認識 List<T>的本質,先來看下上文中我所粘出的代碼:

List<int> list = new List<int>();
for (int i = 0; i < 10; i++)
{
   list.Add(i);
}      
Random r = new Random();
for (int j = 0; j < 100; j++)
{
   int temp;
   int x1 = r.Next(10);
   int x2 = r.Next(10);
   temp = list[x1];
   list[x1] = list[x2];
   list[x2] = temp;
}

在上文中,我對這個List大批特批,現在,我們來重新看下這個List的構造:

public List()
{
   this._items = List<T>._emptyArray;
}

先來看無參的構造方法,當無參的時候,.NET Framework其實是用一個_emptyArray來初 始化了List中的items集合。那麼_emptyArray又是什麼呢?我們繼續向下看:

private static T[] _emptyArray;

恩,他是一個靜態字段,然後我們看下List<T>的靜態構造方法:

static List()
{
   List<T>._emptyArray = new T[0];
}

我們看到,_emptyArray其實是一個T類型的數組,個數為0。那麼也就是說,當我們執行0 參數的構造方法時,系統是把items集合給賦值為了一個T類型的個數為0的數組。

那麼items又是什麼?我們繼續向下看:

public void Add(T item)
{
   if (this._size == this._items.Length)
   {
     this.EnsureCapacity(this._size + 1);
   }
   this._items[this._size++] = item;
   this._version++;
}

這是List<T>中一個Add(T item)方法,但是我們可以從方法中敲出些端倪來。

在這裡,我並不是想解釋這個方法的原理,只是想說,在List中,其實維護這一個items ,然後很多操作,是基於items的操作。

恩,所以在上文中,List<int> list=new List<int>();和Array a=new int[10]();的操作其實差別並不大。

我們肯定還記得在《Effective C#》中有這樣一條規則,就是說:在初始化List之前最好 對List初始化大小。

讓我們從源碼中來找到這一條規則的答案。

private void EnsureCapacity(int min)
{
   if (this._items.Length < min)
   {
     int num = (this._items.Length == 0) ? 4 : (this._items.Length  * 2);
     if (num < min)
     {
       num = min;
     }
     this.Capacity = num;
   }
}

我們來看,在這個方法體中,List會新建一個數組,然後把數組的長度設置為原來的二倍 (如果原有的數組長度為0,那就默認將數組的長度設置為4)。

因此,這種,讓List的方法自己去調用EnsureCapacity(int min)方法,不僅浪費了構造 數組的時間,還浪費了大量的空間(因為將原有的數組空間擴充了二倍)。

因此,請記得:在初始化List之前最好指定List的大小。

為了證明上述的觀點,我們再來隨便看一些代碼:

public int IndexOf(T item)
{
   return Array.IndexOf<T>(this._items, item, 0, this._size);
}

public int FindIndex(int startIndex, int count, Predicate<T>  match)
{
   if (startIndex > this._size)
   {
     ThrowHelper.ThrowArgumentOutOfRangeException (ExceptionArgument.startIndex, ExceptionResource.ArgumentOutOfRange_Index);
   }
   if ((count < 0) || (startIndex > (this._size - count)))
   {
     ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count,  ExceptionResource.ArgumentOutOfRange_Count);
   }
   if (match == null)
   {
     ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
   }
   int num = startIndex + count;
   for (int i = startIndex; i < num; i++)
   {
     if (match(this._items[i]))
     {
       return i;
     }
   }
   return -1;
}

由上面的代碼,我想證明的是:其實List<T>不過是對Array的進一步封裝,說得再 直接點,我願意理解List<T>為Array的可擴充版本,然後擴展了一些方法,諸如 Contains(T item);

那關於Array和List的選擇,我重新做一個說明:

List是基於Array存在的,因此,在創建一個List對象時,需要耗費比Array相對更多的時 間,以及更大的空間,因為List除了初始化內部的items外還需要初始化一些其他的屬性。而 且在方法調用時,這點我沒有證實,只是一個猜測,List需要的是再去調用Array的相關方法 ,因此也許會存在方法調用的時間消耗問題。

因此,我的建議是:

如果初始化時確定大小,那麼就使用Array。

如果初始化時不確定大小,那麼就使用List。當然,其實完全可以自己去實現List中的數 組擴充功能的,也許會更棒,因為我們沒有必要去將Array每次都擴充為原來的二倍。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved