基本數據結構的比較

基本數據結構的比較

基本數據結構

1. 列表(List)

  元素有放入順序,元素可重複。html

數組實現(ArrayList類)

  JDK8源碼中,初始長度是10,每次數組擴展都增長1/2左右。即:前端

private void grow(int minCapacity) {      //minCapacity爲size+1,每次add元素都要檢查
        int oldCapacity = elementData.length;   //擴展前數組的容量
        int newCapacity = oldCapacity + (oldCapacity >> 1);   //擴展後數組的容量約爲原容量1.5倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
優勢:

  能夠經過下標來訪問或者修改元素,對下標訪問的set和get時間複雜度爲O(1);java

缺點:
  1. 插入和刪除的花費開銷較大,除非變更是在ArrayList的末端進行。好比當在第一個位置前插入一個元素,那麼首先要把全部的元素日後移動一個位置;數組擴展時,須要將原數組的元素所有複製到新數組。
  2. 數組要在連續的空間裏存儲集合的元素,因爲數據存儲是連續的,所以支持用下標訪問元素;

數組實現(Vector 類)

  一樣基於數組實現,會在內存中開闢一塊連續的空間來存儲。ArrayList是非線程安全的,效率高;Vector是基於線程安全的,但效率低,而且是方法級別的同步,不是絕對的線程安全
  初始容量10,每次數組擴展到原來容量的2倍(每次擴充的容量大小是能夠設置的,而ArrayList類不支持設定)。算法

鏈表實現(LinkedList類)

  每個元素存儲自己數據的同時還存儲上、下兩個元素的地址(雙向鏈表)。編程

優勢:
  1. 新項的插入和現有項的刪除平均開銷很小O(1)(假設變更項的位置已知),所以提供了addFirst和removeFirst, addLast和removeLast, getFirst 和 getLast 等有效添加、刪除和訪問兩端的項的方法;
  2. 能夠在非連續的內存空間裏面存儲一個集合的元素;
缺點:
  1. 根據索引的訪問時間複雜度爲O(n);
  2. 存放相同多的數據,通常狀況下,數組佔用較小的內存,而鏈表還須要存放其前驅和後繼的空間。

2. 棧(Stack)

  棧,在計算機中運用普遍,好比說JVM,它就是基於棧來執行指令的。棧是限制插入和刪除只能在一個位置上進行的表,該位置是表的末端,叫做棧頂,對棧的基本操做有push(進棧)和pop(出棧),前者至關於插入,後者至關於刪除最後一個元素。棧有時又叫做LIFO(Last In First Out)表,即後進先出。
數組

  棧通常有兩種實現,全部操做時間複雜度O(1):安全

  • 棧的鏈表實現:利用LinkedList類,經過表頂端的元素插入和刪除。
  • 棧的數組實現:模仿ArrayList類,和棧相關的有兩個元素,arrayList數組和topOfStack索引,初始狀態topOfStack==-1,每次進棧一個元素x,topOfStack增1並令arrayList[topOfStack]=x;每次出棧一個元素,咱們置返回值arrayList[topOfStack],並令topOfStack減1。

3. 隊列(Queue)

  對於隊列來講,元素只能從隊列尾插入,從隊列頭訪問和刪除。普通的隊列是一種先進先出(First In First Out,FIFO)的數據結構,而優先隊列中,元素都被賦予優先級。當訪問元素的時候,具備最高優先級的元素最早被刪除。
  隊列也是表,通常有兩種實現,全部操做時間複雜度O(1)(優先隊列是經過大頂堆或者小頂堆實現):數據結構

  • 隊列的鏈表實現:利用LinkedList類,經過表尾端插入元素,前端刪除元素,並記錄隊列中元素個數currentSize。
  • 隊列的數組實現:保留一個數組theArray以及位置front和back,表明隊列的兩端;同時還要記錄隊列中元素個數currentSize。要使一個元素x入隊,則currentSize和back增1,theArray[back]=x;要使一個元素出隊,咱們置返回值theArray[front],且currentSize減1,、front增1。採用循環數組的方式,當front和back到達數組的尾端,他們又繞回開頭。

4. 集合(Set)

  元素無放入順序,元素不可重複(注意:元素雖然無放入順序,可是元素在set中的位置是由該元素的HashCode決定的,其位置實際上是固定的)
  Set接口有兩個實現類:HashSet和LinkedHashSet多線程

  • HashSet:(底層由HashMap實現),HashSet類按照哈希算法來存取集合中的對象,存取速度比較快 ,存入HashSet的對象必須定義hashCode()和equals()來確保對象的惟一性。
  • LinkedHashSet:具備HashSet的查詢速度,且內部使用鏈表維護元素的順序(插入的次序)。因而在使用迭代器遍歷Set時,結果會按元素插入的次序顯示。

  SortedSet接口有一個實現類:TreeSet 底層是經過 TreeMap來實現的(如同HashSet底層是是經過HashMap來實現的同樣),所以兩者的實現方式幾乎徹底同樣。併發

5. 映射(Map)

  元素按鍵值對存儲,通常無放入順序,其中值能夠重複,但鍵是惟一的,不能重複。Map接口有三個實現類:HashMap,Hashtable,LinkeHashMap

  • HashMap:基於散列表實現,使用對象的「散列碼」(hash code)來快速查詢(默認使用的是Object的equals()和hashCode()方法,所以若是須要以本身定義的對象做爲key,須要重寫這兩個方法,可是因爲String字符串的這兩個方法已經重寫,以字符串做爲key能夠不重寫),非線程安全,高效,容許有一個key設爲null,初始容量16,負載因子0.75(好比容量16,能夠存放16*0.75=12個數據,減小衝突),增長方式:通常old*2,因爲容許設置初始容量,同時要保證容量增長後要是2的指數,因此容量增長比較複雜,詳細看 http://www.cnblogs.com/xzwblog/p/7289952.html#_label2_0
  • Hashtable:一樣基於散列表實現,但線程安全(一樣是方法級別的同步),低效,不容許任何key設爲null,初始容量11,負載因子0.75,增長方式是old*2+1
  • LinkeHashMap:LinkedHashMap是HashMap的一個子類,它保留插入的順序。LinkedHashMap採用的hash算法和HashMap相同,可是它從新定義了數組中保存的元素Entry,該Entry除了保存當前對象的引用外,還保存了其上一個插入元素before和下一個插入元素after的引用,從而在哈希表的基礎上又構成了雙向連接列表。
  • SortedMap接口的實現類:TreeMap 的實現是紅黑樹算法,每一個 Entry 都被當成「紅黑樹」的一個節點對待,對key進行排序。插入、刪除和查詢都比較慢,複雜度O(logN),基於hash的複雜度通常爲O(1)。但TreeMap 中的全部 Entry 老是按 key 根據指定排序規則保持有序狀態,TreeSet 中全部元素老是根據指定排序規則保持有序狀態。

HashMap和Hashtable的hash值計算方式也不相同
  HashMap的hash計算參考 http://www.cnblogs.com/xzwblog/p/7289952.html#_label2_0 ,速度更快。
  Hashtable是直接使用對象的hashCode,而且計算在hash表中的索引時直接使用%,以下代碼:

int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;

  而且在高併發環境下,徹底能夠用ConcurrentHashMap來代替Hashtable。

  還有一點不一樣:HashMap去掉了Hashtable 的contains方法,可是加上了containsValue()和containsKey()方法。

如何實現HashMap的同步?
  HashMap能夠經過下面的語句進行同步:Map m = Collections.synchronizeMap(hashMap);,具體而言,該方法返回一個同步的Map,該Map封裝了底層的HashMap的全部方法,使得底層的HashMap即便是在多線程的環境中也是安全的。

比較

  Collection、List、Set、Map都是接口,不能實例化。
  JAVA集合只能存放引用類型的的數據,不能存放基本數據類型。
基本數據類類型:存的是數值自己,java中有四類八種基本數據類型,

  • 第一類:整型 byte short int long
  • 第二類:浮點型 float double
  • 第三類:邏輯型 boolean
  • 第四類:字符型 char

引用類型變量:存放的是數據的引用地址,並非數據的自己,引用類型變量是以間接方式去獲取數據。引用類型變量都指向對象,如:數組、類、字符串等。

  Collection接口的經常使用方法:

  • boolean add(Object o)
  • void clear()
  • boolean isEmpty()
  • boolean contains(Object o)
  • Iterartor iterator()
  • boolean remove(Object o)
  • int size()
  • int hashCode();
  • boolean equals(Object o);
  • Object[] toArray() : 返回一個數組,該數組中包括集合中的全部元素

  List,Set是繼承自Collection接口,Map不是。因此記不清隊列和棧的添加和刪除函數時,一概用add和remove。

總結:
  1. 若是涉及到堆棧,隊列等操做,應該考慮用List,對於須要快速插入,刪除元素,應該使用LinkedList,若是須要快速隨機訪問元素,應該使用ArrayList或者直接數組。
  2. 若是程序在單線程環境中,或者訪問僅僅在一個線程中進行,考慮非同步的類,其效率較高,若是多個線程可能同時操做一個類,應該使用同步的類。
  3. 在除須要排序時使用TreeSet,TreeMap外,都應使用HashSet,HashMap,由於他們的效率更高。
  4. 要特別注意對哈希表的操做,做爲key的對象要正確複寫equals和hashCode方法。
  5. 儘可能返回接口而非實際的類型,如返回List而非ArrayList,這樣若是之後須要將ArrayList換成LinkedList時,客戶端代碼不用改變。這就是面向接口編程。
注意:

一、Collection沒有get()方法來取得某個元素,只能經過iterator()遍歷元素。 二、Set和Collection擁有如出一轍的接口。 三、List,能夠經過get()方法來一次取出一個元素,使用數字來選擇一堆對象中的一個,get(0)...。(add/get)。 四、通常使用LinkedList構造棧stack、隊列queue。 五、Map用 put(k,v) / get(k),還可使用containsKey()/containsValue()來檢查其中是否含有某個key/value。HashMap會利用對象的hashCode來快速找到key。 六、Map中元素,能夠將key序列、value序列單獨抽取出來。使用keySet()抽取key序列,將map中的全部keys生成一個Set。使用values()抽取value序列,將map中的全部values生成一個Collection。   爲何一個生成Set,一個生成Collection?那是由於,key老是獨一無二的,value容許重複。

相關文章
相關標籤/搜索