java集合類,ArrayList、HashSet 、LinkedLlist

 

ArrayList是實現了基於動態數組的數據結構,LinkedList基於鏈表的數據結構。

讀寫效率

HashSet讀寫是最慢的,因爲HashSet每次add要判斷hashcode,HashSet兩種循環中iterator 方式不穩定,不過總是比foreach要快一點。ArrayList讀寫效率其次,在ArrayList中間插入或刪除一個元素,要改變整個集合中該元素後面所有元素的下標位置。LinkedList讀寫速度最快,LinkedList的中間插入或刪除一個元素的開銷是固定的,只需要對插入位置前後的元素指針進行修改。

去除集合內部重複元素

如果要用HashSet對集合中元素實現去重,不推薦這種做法,因爲hash算法的速度比遍歷一個ArrayList要慢得多。即使在ArrayList中進行兩次循環,所消耗的時間仍然快於直接使用HashSet的構造方法。

String stocks="001,002,003,002";
LinkedHashSet stocksSet=new LinkedHashSet<(Arrays.asList(stocks.split(",")));
return String.join(",",stocksSet);
           

 ArrayList中的remove方法會將元素前移一個位置,這裏使用倒序刪除避免漏掉元素或者報併發錯誤。

String stocks="001,002,003,002";
ArrayList list=new ArrayList<>(Arrays.asList(stocks.split(",")));
for  ( int  i  =   0 ; i  <  list.size()  -   1 ; i ++ )  {       
   for  ( int  j  =  list.size()  -   1 ; j  >  i; j -- )  {       
      if  (list.get(j).equals(list.get(i)))  {       
              list.remove(j);       
       }        
    }        
}        
return String.join(",",list);      

 

訪問元素

ArrayList的內部實現是基於基礎的對象數組的,因此,它使用get方法訪問列表中的任意一個元素時(random-access),它的速度要比LinkedList快。當然也包括使用 for(int i<0;i<list.size();i++) 這種循環方式。LinkedList中的get方法是按照順序從列表的一端開始檢查,直到另外一端。對LinkedList而言,利用Collections.reverse方法對列表進行順序反轉時,LinkedList性能要好些。 

空間複雜度

ArrayList和LinkedList在空間複雜度上都具有一定的空間上的冗餘。ArrayList的空間浪費主要體現在在list列表的結尾預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗相當的空間,在LinkedList中有一個私有的內部類,定義如下:

private static class Entry {
         Object element; 
         Entry next; 
         Entry previous; 
     }

每個Entry對象reference列表中的一個元素,同時還有在LinkedList中它的上一個元素和下一個元素。一個有1000個元素的LinkedList對象將有1000個鏈接在一起的Entry對象,每個對象都對應於列表中的一個元素。這樣的話,在一個LinkedList結構中將有一個很大的空間開銷,因爲它要存儲這1000個Entity對象的相關信息。

ArrayList使用一個內置的數組來存儲元素,這個數組的起始容量是10.當數組需要增長時,新的容量按如下公式獲得:新容量=(舊容量*3)/2+1,也就是說每一次容量大概會增長50%。這就意味着,如果你有一個包含大量元素的ArrayList對象,那麼最終將有很大的空間會被浪費掉,這個浪費是由ArrayList的工作方式本身造成的。如果沒有足夠的空間來存放新的元素,數組將不得不被重新進行分配以便能夠增加新的元素。對數組進行重新分配,將會導致性能急劇下降。如果我們知道一個ArrayList將會有多少個元素,我們可以通過構造方法來指定容量。我們還可以通過trimToSize方法在ArrayList分配完畢之後去掉浪費掉的空間。

ArrayList和LinkedList循環遍歷方式的性能分析

1、for-each

List<String> testList = new ArrayList<String>();

for (String tmp : testList)

{  

  //use tmp;

}


2、迭代器方式這種遍歷方式是最常用的遍歷方式,因爲書寫比較方便,而且不需要考慮數組越界的問題,Effective-Java中推薦使用此種寫法遍歷。

List<String> testList = new ArrayList<String>();

for (Iterator<String> iterator = testList.iterator(); iterator.hasNext();)

{

    //String tmp = iterator.next();

}

 

3、下標遞增或遞減循環

List<String> testList = new ArrayList<String>();

for (int i = 0; i < testList.size(); i++;)

{

    //String tmp = testList.get(i);

}


以上三種遍歷方式是在使用list時最常用的方式,那麼這三種方式在遍歷的速度已經性能上又有什麼區別呢?我們從數據的底層實現上來進行分析。下標遞增或者遞減循環是最早接觸到的遍歷方式,會經常出現數組越界的問題。

List底層儲存都是使用數組來進行存儲的,ArrayList是直接通過數組來進行存儲,而LinkedList則是使用數組模擬指針,來實現鏈表的方式,因此從這裏就可以總結出,ArrayList在使用下標的方式循環遍歷的時候性能最好,通過下標可以直接取數據,速度最快。而LinkedList因爲有一層指針,無法直接取到對應的下標,因此在使用下標遍歷時就需要計算對應的下面是哪個元素,從指針的頭一步一步的走,所以效率就很低。想到指針就會聯想到迭代器,迭代器可以指向下一個元素,而迭代器就是使用指針來實現的,因此LinkedList在使用迭代器遍歷時會效率最高,迭代器直接通過LinkedList的指針進行遍歷,ArrayList在使用迭代器時,因爲要通過ArrayList先生成指針,因此效率就會低於下標方式,而for-each又是在迭代器基礎上又進行了封裝,因此效率會更低一點,但是會很接近迭代器。

在進行list遍歷時,如果是對ArrayList進行遍歷,推薦使用下標方式,如果是LinkedList則推薦使用迭代器方式。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章