Java 集合框架(二)—— ArrayList

shaopiing發表於2016-05-29

二、陣列列表 —— ArrayList

  

  1、構造方法

  ArrayList 是 Java 中的動態陣列,底層實現就是物件陣列,只不過陣列的容量會根據情況來改變。

  它有個帶 int 型別引數的構造方法,根據傳入的引數,擴充套件初始化的陣列容量,這個方法是推薦使用的,因為如果預先知道陣列的容量,可以設定好初始值,而不用等每次容量不夠而擴容,減少 Arrays.copyOf 的次數:

  

  它的很多方法的實現都是依靠 Arrays 這個工具類完成的,足以體現它與陣列之間的密切關係。

  比如有個帶 Collection 型別的構造方法,實現如下:

  

  2、常用方法

  1)  trimToSize 方法

  Trims the capacity of this ArrayList instance to be the list's current size.

  An application can use this operation to minimize the storage of an ArrayList instance.

  該方法可以去掉 ArrayList 佔用的多餘的空間或記憶體,因為 ArrayList 每次擴容後總會有所剩餘,如果陣列很大,佔用的多餘的空間會比較大,記憶體不夠時可以使用此方法。

  2)ensureCapacity 方法

  public void ensureCapacity(int minCapacity)

  Increases the capacity of this ArrayList instance, if necessary, to ensure that it can hold at least the number of elements specified by the minimum capacity argument.

  除了在初始化 ArrayList 的時候可以事先定義一個給定的容量之外,還可以用此方法提高 ArrayList 的初始化速度。看下面的例子:

 1 public static void main(String[] args) {
 2         int n = 100000;
 3         String str = "hello google";
 4 
 5         // 沒有呼叫 ensureCapacity() 方法初始化 ArrayList 物件
 6         ArrayList<String> list = new ArrayList<>();
 7         long startTime = System.currentTimeMillis();
 8         for (int i = 0; i <= n; i++) {
 9             list.add(str);
10         }
11         long endTime = System.currentTimeMillis();
12         System.out.println("time: " + (endTime - startTime) + " ms");
13 
14         // 呼叫 ensureCapacity() 方法初始化 ArrayList 物件
15         list = new ArrayList<>();
16         startTime = System.currentTimeMillis();
17         list.ensureCapacity(n);
18         for (int i = 0; i < n; i++) {
19             list.add(str);
20         }
21         endTime = System.currentTimeMillis();
22         System.out.println("time: " + (endTime - startTime) + " ms");
23     }

  結果為:

  

  3)isEmpty 方法

  注意此方法是判斷是否為空,不是是否為 null

public boolean isEmpty() {
  return size == 0;
}

  4)indexOf 、lastIndexOf 和 contain 方法

  indexOf 方法返回 list 中首次出現給定物件的索引值(從 0 開始),如果不存在則返回 -1。

  lastIndexOf 方法返回 list 中最後一次出現給定物件的索引值(從 size - 1 開始),如果不存在則返回 -1。

  contain 方法 引數為 Object o,判斷 list 中是否包含給定的物件,存在則返回 true,原始碼如下:

  

  5)add,get 和 set 方法

  三個很簡單的方法,區別在於:add 方法是陣列長度 +1,將給定物件放在最後的位置,set 方法是替換指定索引位置的元素,get 方法則是獲取指定索引位置的元素。

  6)remove 方法

  刪除指定索引位置的元素或者指定元素,不推薦使用,對陣列操作比較複雜,如果你使用了此方法,說明你應該考慮用 LinkedList 了。

  3、最佳使用建議

  1)ArrayList 是 Array 的複雜版本

  ArrayList 內部封裝了一個 Object 型別的陣列,從一般的意義來說,它和陣列沒有本質的差別,甚至於 ArrayList 的許多方法,如 Index、IndexOf、Contains、Sort 等都是在內部陣列的基礎上直接呼叫 Array 的對應方法。

  2)內部的 Object 型別的影響

  對於一般引用型別來說,這部分的影響不大,但是對於值型別,往 ArrayList 裡面新增和修改元素,都會引起裝箱和拆箱操作,頻繁的操作可能會影響一部分效率。

  3)陣列擴容

  這是對 ArrayList 效率影響比較大的一個因素。 

  每當執行 Add、AddRange、Insert、InsertRange 等新增元素的方法,都會檢查內部陣列的容量是否不夠了,如果是,它就會以當前容量的兩倍來重新構建一個陣列,將舊元素 Copy 到新陣列中,然後丟棄舊陣列,在這個臨界點的擴容操作,應該來說是比較影響效率的。 
  例 1:比如,一個可能有 200 個元素的資料動態新增到一個以預設 16 個元素大小建立的 ArrayList 中,將會經過: 
  16*2*2*2*2 = 256 
  四次的擴容才會滿足最終的要求,那麼如果一開始就以 ArrayList list = new ArrayList(210) 的方式建立 ArrayList,不僅會減少 4 次陣列建立和 Copy 的操作,還會減少記憶體使用。

  例 2:預計有 30 個元素而建立了一個 ArrayList: 
  ArrayList List = new ArrayList(30); 
  在執行過程中,加入了 31 個元素,那麼陣列會擴充到 60 個元素的大小,而這時候不會有新的元素再增加進來,而且有沒有呼叫 TrimSize 方法,那麼就有 1 次擴容的操作,並且浪費了 29 個元素大小的空間。如果這時候,用 ArrayList list = new ArrayList(40) 那麼一切都解決了。 
  所以說,正確的預估可能的元素,並且在適當的時候呼叫 TrimSize 方法是提高 ArrayList 使用效率的重要途徑。 

特別感謝:

1、Java 中 ArrayList 類的用法

2、《Thinking In Java》

3、《Core Java Volume I》

相關文章