集合總結--ArrayList、LinkedList、HashMap


 
一、概述
       ArrayList:數組集合。 查詢、修改、新增(尾部新增)快,刪除、新增(隊列中間)慢,適用於查詢、修改較多的場景。
       LinkedList:雙向鏈表集合。查詢、修改慢(需要遍歷集合),新增,刪除快(只需要修改前后節點的鏈接即可),適用於新增、刪除較多的場景。
       HashMap:結合數組和鏈表的優勢,期望做到增刪改查都快速,時間復雜度接近於O(1)。當hash算法較好時,hash沖突較低。適用於增刪改查所有場景。
 
 
二、分敘
    ArrayList
  • ArrayList底層實現是基於數組的,因此對指定下標的查找和修改比較快,但是刪除和插入操作比較慢。

  • 構造ArrayList時盡量指定容量,減少擴容時帶來的數組復制操作,如果不知道大小可以賦值為默認容量10。

  • 每次添加元素之前會檢查是否需要擴容,每次擴容都是增加原有容量的一半。(擴容是創建一個新的數組,並將原來的數組元素遷移到新數組中)

  • 每次對下標的操作都會進行安全性檢查,如果出現數組越界就立即拋出異常。

  • ArrayList的所有方法都沒有進行同步,因此它不是線程安全的

  • 以上分析基於JDK1.7,其他版本會有些出入,因此不能一概而論

 
    LinkedList
   數據結構
private static class Node<E> {
  E item;          //元素
  Node<E> next;    //下一個節點
  Node<E> prev;    //上一個節點

  Node(Node<E> prev, E element, Node<E> next) {
      this.item = element;
      this.next = next;
      this.prev = prev;
  }
}
  • LinkedList是基於雙向鏈表實現的,不論是增刪改查方法還是隊列和棧的實現,都可通過操作結點實現

  • LinkedList無需提前指定容量,因為基於鏈表操作,集合的容量隨着元素的加入自動增加(無需執行默認長度,也沒有擴容需求)

  • LinkedList刪除元素后集合占用的內存自動縮小,無需像ArrayList一樣調用trimToSize()方法

  • LinkedList的所有方法沒有進行同步,因此它也不是線程安全的,應該避免在多線程環境下使用

  • LinkedList根據index查詢時采取的是二分法,即index小於總長度一半時從鏈表頭開始往后查找,大於總長度一半時從鏈表尾往前查找。如果是根據元素查找,則需要從頭開始遍歷

  • 以上分析基於JDK1.7,其他版本會有些出入,因此不能一概而論。

 
 
  HashMap
  數據結構
static class Entry<K,V> implements Map.Entry<K,V> {
  final K key;      //
  V value;          //
  Entry<K,V> next;  //下一個Entry的引用
  int hash;        //哈希碼
 
  ...              //省略下面代碼
}
  哈希圖
    • 哈希表是由數組和單項鏈表共同構成的一種結構,上圖中一個數組元素鏈表存在多個元素,說明存在hash沖突,理想情況下每個數組元素只應包含一個元素
    • 擴容原因:HashMap默認的初始容量為16,默認的加載因子是0.75。而threshold是集合能夠存儲的鍵值對的閥值,默認是初始容量*加載因子,也就是16*0.75=12,當鍵值對要超過閥值時,意味着這時候的哈希表已處於飽和狀態,再繼續添加元素就會增加哈希沖突,從而使HashMap的性能下降。
    • 每次擴容都是增加原有容量的一倍。(擴容是創建一個新的數組,並將原來的數組元素遷移到新數組中,根據hash值重新分配)
    • hash值的計算方式(字符串是單獨的計算方式,擾動函數就是把所有東西雜糅到一起,提高隨機性
//生成hash碼的函數
final int hash(Object k) {
  int h = hashSeed;
  //key是String類型的就使用另外的哈希算法
  if (0 != h && k instanceof String) {
      return sun.misc.Hashing.stringHash32((String) k);
  }
  h ^= k.hashCode();
  //擾動函數
  h ^= (h >>> 20) ^ (h >>> 12);
  return h ^ (h >>> 7) ^ (h >>> 4);
}
 
 
三、總結
       概述中已經描述各個集合的適用場景,這里重點說一下HashMap。HashMap可以通過hash值快速定位到數組下標,執行新增、修改、刪除操作。當hash算法較好(hash沖突較少)時,增刪改查的時間復雜度都是O(1)。但是如果鏈表較長,則會增加增刪改查的時間復雜度O(鏈表長度)。原則就是盡量減少hash沖突,並預先估算hashmap長度,減少擴容操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM