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

.NET,你忘記了麼?(三)——關於Array和List的使用

編輯:關於.NET

之前,一直在談.NET框架方面的問題,今天來談談關於Array和List的使用問題,這應該 算是屬於算法的最基礎的東西了。只是提醒大家對這個問題稍加注意。

寫這個是因為一個同學的求助,事情是這樣的,他去負責公司的一個培訓模塊,在培訓模 塊中,有一個功能是自動成卷。然後,我們會很容易地想到洗牌算法。於是我給他大概解釋 了洗牌算法的過程和步驟,然後他給出了這樣的代碼,還很驕傲地告訴我,他使用了泛型… …

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;
}

我委婉地告訴他,他這個方法不好,然後寫下了下面的代碼:

int[] array = new int[10];
for (int i = 0; i < 10; i++)
{
   array[i] = 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 = array[x1];
   array[x1] = array[x2];
   array[x2] = temp;
}

他很不屑地對我說,不是都一樣麼!而且還在以使用了泛型為豪!我無語……

我僅僅把List(鏈表)換成了Array(數組),有人會說,這樣的關系大麼?

讓我們先來簡單地回顧一下基礎知識。

Array和List都屬於順序表。

Array是一段連續的存儲結構,如int[] i=new int[3],i其實記錄的是數組的首地址,而 i[1]其實相當於在i的地址的基礎上加上1個整數的地址偏移,然後再取這塊地址中的值。也 就是相當於*(&i[0]+4);

而List則是不連續的存儲結構,List的每個節點都有著一個Next屬性,這個屬性則記錄著 他的下一個節點的地址。也就是說當我們想找第100個節點的時候,他還是需要從第一個節點 ,然後做99次Next操作,才能找到list[99]節點。這是個蠻痛苦的過程。

很多人會說,那無論是List還是Array,不都是一個索引麼!讓我們來請出IL:

先來看Array查找某個元素的IL:

IL_0020:  ldloc.0

IL_0021:  ldc.i4.3

IL_0022:  ldelem.i4

IL_0023:  stloc.2

然後讓我們來看下List查找某個元素的IL:

IL_0022:  ldloc.0

IL_0023:  ldc.i4.3

IL_0024:  callvirt   instance !0 class [mscorlib] System.Collections.Generic.List`1<int32>::get_Item(int32)

IL_0029:  stloc.2

我們可以發現,雖然他們的寫法是一樣的,但是在IL中卻有著很大的差別。

通過這兩段IL,我只是希望證明List和Array對索引元素的方式是不同的。當然,我們無 從知道Microsoft對List方法get_Item的實現。但是我們不難想象:

因為List是一個鏈表,所以我需要從第一個元素開始逐個Next到所需索引的元素。這是一 個耗時的過程。

因此,在使用洗牌算法時,使用List是個很差勁的做法。再進一步說,當我們需要大量的 索引步驟時,使用List是個很差勁的做法。

那List和Array各自應該用在什麼情況下呢?我來做個簡單的總結:

1. 從空間擴展角度上來說:

數組必須要在初始化時分配固定的大小,比如說int[] a=new int[3];如果我們僅僅寫 int[] a=new int[];編譯器就會無情地給我們報錯。但是List由於空間不必連續,所以無須 指定初始大小。

總結1: 當不確定大小時,最好使用List代替Array。

2. 從操作角度上來看:

關於索引這個就不贅述了。

總結2:當需要大量的查找操作時,最好使用Array。

對於插入(刪除)操作,很多人是從插入(刪除)的時間上分析,說List優於Array,我 覺得是不合理的。

更合理的解釋應該是從兩個角度分析(以插入為例):

<1> 指定位置插入指定元素:

對於Array講,有兩套解決方案:

A. 使用一個新數組,N+1個元素重新賦值的過程。一個for循環,時間復雜度O(n)。

B. 在原數組上操作,那麼首先需要為該數組預留空間,這是個很難辦的事情。而且其後 續元素的移動耗費時間復雜度仍未O(n)。

對於List來講,很多人說復雜度就是O(1)。這其實是不合理的,因為List插入元素固然容 易,但是在指定位置的插入,需要一個時間復雜度為O(n)的查找過程。

但是只考慮時間復雜度是不夠的,我們要考慮總體的情況。如果使用新數組,不僅浪費了 新的空間,而且需要反復的賦值過程,是N+1次。如果不使用新數組,預留空間實在太麻煩, 因此綜上所述,還是List好。

(補充下:很多同事和朋友都問我,說為什麼要學那麼多的排序和搜索算法,排序學個快 速排序,搜索學個二分搜索,這樣就夠了呗!但是我想說的是,所說的最快,不過是他們的 平均(或最壞)情況下的時間復雜度,並不能代表通用的情況,我們在實際工作中,還是要 根據實際情況去選擇最適用的算法,這就是我們學習很多算法的目的)

<2> 給出前一個節點,然後在後面插入元素。這個我的意思就是不僅僅給出了 PreviousNode的Value,還給出了他的Next。這個情況我就不廢話了,List的優勢太大了。可 是在實際情況中,這種情況的可能性幾乎為零。

因此,總結3:當需要進行頻繁的插入,刪除操作時,最好使用List代替Array。

另外,給出個不太重要的補充,由於List需要存儲他下一個節點的地址,所以List比 Array相對起來浪費了更多的空間。

這次關於List和Array的用法區別就先寫到這。希望大家有什麼不同意見的多多指教!

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