FCL(3):: ArrayList & List (1)

來源:互聯網
上載者:User

ArrayList是以前最常用的一個Collection. <<.net設計規範>>第八章一條:  要優先使用集合,而不是優先使用數組. 因ArrayList比Array好用多了:)
記得以前面試的時候主考官問我用過ArrayList沒,答:用過; 問:與Array有什麼區別.答:這個...這個...記不起來了.事後坐公交的時候,突然想到ArrayList不就是Array的List,數組鏈表嗎.狂拍自己腦袋.所幸主考官還是讓我pass了,有幸進入到現在的公司.

用反射看了一下ArrayList,其實就是對一個Array的封裝.就如<<Maximizing .Net Performance>>:  "The only collection implemented directly at a runtime level is System.Array, and all other collection types are implemented using Array." ArrayList底層用的資料對象就是一個Object的 Array.只不過它提供了一個List功能的介面.初始化預設的大小是4,以前咋聽說是16呢.

當我們調用其Add()方法時,首先會去判斷一下是否有足夠的空間  if (this._size == this._items.Length), 不夠的話就調用EnsureCapacity()方法,確保其已指派足夠的空間.在申請新的一片記憶體空間時,會調用Array.Copy()方法將原有的資料複製到新的地址.(哈哈,跟我以前大學學C#做過的一個練習,實現的方式是一樣的).這個不僅讓我想到了一個效能問題.資料結構裡面的Array的List模型可非如此.應該是當申請完一片新的空間後,原來舊空間的最後一個位置通過指標連結到新空間的首地址.這樣的話就不需要拷貝原來資料到新的空間地址裡了.而且資料可以儲存在不連貫的空間上.更加合理地利用了空間.當然這樣的實現會更複雜些,帶來的最大好處就是頻繁擴大空間時不需要拷貝一次舊的資料.當然通過索引來訪問資料,可能沒有用一個數組那樣訪問那麼快了.
所以說在ArrayList初始化的時候,應該給它一個初始的Capacity(如果大致知道需要容器的大小,或者在需要很大的情況下,盡量稍微分大一點),盡量避免多次重新分配空間,避免頻繁拷貝資料帶來的開銷.

接著說說ArrayList.InsertRange(int index, ICollection c) 和 List<T>.InsertRange(int index, IEnumerable<T> collection).仔細觀察一下兩個方法,它們第二個參數的類型是不一樣的.ICollection是繼承自IEnumerable的.這個改變體現了<<.net設計規範>>第八章一條:  要用最範的類型作為參數類型.另一條是:  避免使用ICollection<T>或ICollection來做參數,如果其目的只是為了訪問該介面的Count屬性.還舉了個例子,說 可以考慮用IEnumerabler作為參數,然後動態地檢查一下對象,看它有沒有實現ICollection.
在此也有個概念可以得到明確:實現了IEnumerabler介面的類就是一個集合,不要以為ICollection介面上有個Collection就認為集合一定要實現此介面.(不過一般都會實現此介面 :))
再說List<T>.InsertRange()方法,因為是 IEnumerable<T>型別參數,我們拿不到它的Count,於是會先將其轉型為ICollection<T>,看是否成功,成功的話就可以只需調用一次EnsureCapacity(),一次性的分配足夠的空間就行了.然後調用ICollection<T>.CopyTo()方法一次性Copy就好了.如果不是,那隻能逐個逐個遍曆再Insert了,效能肯定是要損失了.不過貌似看到的集合類都是直接或間接實現了ICollection或ICollection<T>介面.

這裡讓我想到了前幾天跟Leader的一個關於設計的爭論.焦點在於他說這種向下轉型(如IEnumerabler --> ICollection)代碼不優雅,而需要設計出一個輔助類去獲得一個真正我們想要的子類型.而我覺得這樣添加一些額外的操作卻根本沒帶來什麼好處,向下轉型本身就很簡潔,很平常的事.理由就是: 在設計架構的時候它為了實現通訊必須提供它自己的契約:一組介面,而我們在應用的時候需要去擴充我們自己的功能,於是在實現了架構的契約後會提供一組自己的介面.但同時架構在通訊的時候只知道自己定的契約,而不知道我們的類,我們擴充的功能,所以有時候向下轉型是避免不了,也是不為過的.
再看List<T>.InsertRange()方法,因為它定義的參數類型是IEnumerable<T>,那麼它就可以接受更寬泛的參數來源.然而在其內部實現的時候,雖然可以完全用IEnumerable<T>操作,遍曆它然後一個一個調用Insert.但這樣的效能肯定是要被人唾棄的.一個簡單的向下轉型,轉為ICollection<T>就能帶來效能的提升,何樂而不為呢.
我現在就可以更理直氣壯地說,瞧FCL裡面實現也是避免不了向下轉型,你能說這樣一定就不優雅了?

下一片繼續探討一下同步的ArrayList,和一個搞笑的GetRange()方法.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

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.