JAVA常用資料結構及原理分析
集合父介面Collection,Map和集合工具類Collections
集合框架Collection的三種主要實現如下:List(列表),Set(雜湊集,有一個key-value的Map進行維護,其中key值保證Set集合裡元素的唯一性),Queue(佇列,先進先出,底層實現可以用List列表或者LinkedList連結串列)
集合框架的另外一種資料型別的總介面是Map,基於Key-Value進行儲存資料的,其中Key鍵值是不可重複的,主要是通過類的hashCode()和equal()進行保證的
Collections,它封裝了所有集合的關於演算法操作的具體實現靜態方法:二分查詢,找出集合最大值,集合最小值,打亂集合,以及生成不可修改集合,同步集合(多執行緒環境下使用),其主要方法API如下:
由於大部分的集合介面實現類都是不同步的,可以使用Collections.synchronized*方法建立同步的集合類物件。
如建立一個同步的List:
List synList = Collections.synchronizedList(new ArrayList());
其實現原理就是重新封裝new出來的物件,操作物件時用關鍵字synchronized同步。看原始碼很容易理解。
Collections部分原始碼:
//Collections.synchronizedList返回的是靜態類SynchronizedCollection的例項,最終將new出來的ArrayList物件賦值給了Collection<e> c。
static class SynchronizedCollection<e> implements Collection<e>, Serializable {
final Collection<e> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<e> c) {
if (c== null )
throw new NullPointerException();
this .c = c;
mutex = this ;
}
//...
public boolean add(E e) {
//鎖定訊號量才可以對集合進行操作,在多執行緒環境下來進行同步,呼叫的還是原來的方法
//操作集合時簡單呼叫原本的ArrayList物件,只是做了同步
synchronized (mutex) { return c.add(e);}
}
//...
}
List(列表)
List列表允許儲存相同元素,插入元素和按照下標獲取元素方便,具體實現有ArrayList,LinkedList和Vector,
-
ArrayList底層基於陣列實現的,按順序儲存元素以及快速按照元素下標進行獲取元素,不可同步的;
-
而Vector底層也是陣列,可進行同步操作,在多執行緒環境下可以使用;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
//可以看出新增的物件放到elementData陣列中去了
elementData[size++] = e;
return true;
}
//ArrayList.remove
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
//移除元素時陣列產生的空位由System.arraycopy方法將其後的所有元素往前移一位,System.arraycopy呼叫虛擬機器提供的本地方法來提升效率
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // Let gc do its work
return oldValue;
}
//Vector add方法上多了synchronized關鍵字
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
- LinkedList是連結串列可以在具體下標位置刪除和新增元素,在許多需要根據具體下標新增和刪除元素的應用場景下比ArrayList有更好的效能,遍歷比線性錶慢。
public class LinkedList<e>
extends AbstractSequentialList<e>
implements List<e>, Deque<e>, Cloneable, java.io.Serializable
{
//頭尾節點
transient Node<e> first;
transient Node<e> last;
}
//節點類
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;
}
}
Map(儲存鍵值對,key唯一)
HashMap結構的實現原理是將put進來的key-value封裝成一個Entry物件儲存到一個Entry陣列中,位置(陣列下標)由key的雜湊值與陣列長度計算而來。如果陣列當前下標已有值,則將陣列當前下標的值指向新新增的Entry物件。
public class HashMap<k,v>
extends AbstractMap<k,v>
implements Map<k,v>, Cloneable, Serializable
{
transient Entry<k,v>[] table;
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
//遍歷當前下標的Entry物件鏈,如果key已存在則替換
for (Entry<k,v> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
addEntry(hash, key, value, i);
return null;
}
}
static class Entry<k,v> implements Map.Entry<k,v> {
final K key;
V value;
Entry<k,v> next;
int hash;
}
TreeMap是由Entry物件為節點組成的一顆紅黑樹,put到TreeMap的資料預設按key的自然順序排序,new TreeMap時傳入Comparator自定義排序。
HashMap和TreeMap的區別
(1)HashMap:適用於在Map中插入、刪除和定位元素。
(2)Treemap:適用於按自然順序或自定義順序遍歷鍵(key)。
(3)HashMap通常比TreeMap快一點(樹和雜湊表的資料結構使然),建議多使用HashMap,在需要排序的Map時候才用TreeMap.
(4)HashMap 非執行緒安全 TreeMap 非執行緒安全
(5)HashMap的結果是沒有排序的,而TreeMap輸出的結果是排好序的
Set(保證容器內元素唯一性,插入刪除效率高)
Set結構其實就是維護一個Map來儲存資料的,利用Map結構key值唯一性
HashSet的底層通過HashMap實現的,而HashMap在1.7之前使用的是陣列+連結串列實現,在1.8+使用的陣列+連結串列+紅黑樹實現。其實也可以這樣理解,HashSet的底層實現和HashMap使用的是相同的方式,因為Map是無序的,因此HashSet也無法保證順序。HashSet的方法也是藉助HashMap的方法來實現的。
public class HashSet<e>
extends AbstractSet<e>
implements Set<e>, Cloneable, java.io.Serializable
{
//無意義物件來作為Map的value
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
}
TreeSet實現了SortedSet介面,它是一個有序的集合類,TreeSet的底層是通過TreeMap實現的。TreeSet並不是根據插入的順序來排序,而是根據實際的值的大小來排序。。
陣列和連結串列的區別
不同:
- 連結串列是鏈式的儲存結構;陣列是順序的儲存結構 連結串列通過指標來連線元素與元素,陣列則是把所有元素按次序依次儲存。
- 連結串列的插入刪除元素相對陣列較為簡單,不需要移動元素,且較為容易實現長度擴充,但是尋找某個元素較為困難;
陣列尋找某個元素較為簡單,但插入與刪除比較複雜,由於最大長度需要再程式設計一開始時指定,故當達到最大長度時,擴充長度不如連結串列方便。
相同:
兩種結構均可實現資料的順序儲存,構造出來的模型呈線性結構。
連結串列是什麼
連結串列是一種上一個元素的引用指向下一個元素的儲存結構,連結串列通過指標來連線元素與元素;連結串列就是將一系列不連續的記憶體聯絡起來,將那種碎片記憶體進行合理的利用,解決空間的問題。
什麼是hash碰撞?
HashMap中用的最多的方法就屬put() 和 get() 方法;HashMap的Key值是唯一的,那如何保證唯一性呢?我們首先想到的是用equals比較,沒錯,這樣可以實現,但隨著內部元素的增多,put和get的效率將越來越低,這裡的時間複雜度是O(n),假如有1000個元素,put時最差情況需要比較1000次。實際上,HashMap很少會用到equals方法,因為其內通過一個雜湊表管理所有元素,雜湊是通過hash單詞音譯過來的,也可以稱為雜湊表,雜湊演算法可以快速的存取元素,當我們呼叫put存值時,HashMap首先會呼叫Key的hash方法,計算出雜湊碼,通過雜湊碼快速找到某個存放位置(桶),這個位置可以被稱之為bucketIndex,但可能會存在多個元素找到了相同的bucketIndex,有個專業名詞叫碰撞,當碰撞發生時,這時會取到bucketIndex位置已儲存的元素,最終通過equals來比較,equals方法就是碰撞時才會執行的方法,所以前面說HashMap很少會用到equals。HashMap通過hashCode和equals最終判斷出Key是否已存在,如果已存在,則使用新Value值替換舊Value值,並返回舊Value值,如果不存在 ,則存放新的鍵值對<K, V>到bucketIndex位置。
碰撞:多個元素計算得出相同的hashCode,在put時出現衝突。
HashMap 碰撞問題處理:
Java中HashMap是利用“拉鍊法”處理HashCode的碰撞問題。在呼叫HashMap的put方法或get方法時,都會首先呼叫hashcode方法,去查詢相關的key,當有衝突時,再呼叫equals方法。hashMap基於hasing原理,我們通過put和get方法存取物件。當我們將鍵值對傳遞給put方法時,他呼叫鍵物件的hashCode()方法來計算hashCode,然後找到bucket(雜湊桶)位置來儲存物件。當獲取物件時,通過鍵物件的equals()方法找到正確的鍵值對,然後返回值物件。HashMap使用連結串列來解決碰撞問題,當碰撞發生了,物件將會儲存在連結串列的下一個節點中。hashMap在每個連結串列節點儲存鍵值對物件。當兩個不同的鍵卻有相同的hashCode時,他們會儲存在同一個bucket位置的連結串列中。鍵物件的equals()來找到鍵值對。
hashcode是什麼?
- hashcode就是通過hash函式得來的,通俗的說,就是通過某一種演算法得到的,hashcode就是在hash表中有對應的位置。
- hash是一個函式,該函式中的實現就是一種演算法,就是通過一系列的演算法來得到一個hash值,這個時候,我們就需要知道另一個東西,hash表,通過hash演算法得到的hash值就在這張hash表中,也就是說,hash表就是所有的hash值組成的,有很多種hash函式,也就代表著有很多種演算法得到hash值,如上面截圖的三種,等會我們就拿第一種來說。
相關文章
- 圖解:Java 中的資料結構及原理圖解Java資料結構
- 圖解Java常用資料結構圖解Java資料結構
- 【資料結構】ArrayList原理及實現資料結構
- Java刷題常用的資料結構總結Java資料結構
- Java常用資料結構之Map-HashMapJava資料結構HashMap
- Java常用資料結構之Set之TreeSetJava資料結構
- Java常用資料結構之Stack&VectorJava資料結構
- 圖解Java中的資料結構及原理,傻瓜也能看懂!圖解Java資料結構
- 圖解 Java 中的資料結構及原理,傻瓜也能看懂!圖解Java資料結構
- 資料結構與演算法——常用高階資料結構及其Java實現資料結構演算法Java
- Java HashMap原理及內部儲存結構JavaHashMap
- Python常用資料結構(列表)Python資料結構
- Go常用的資料結構Go資料結構
- Java資料結構Java資料結構
- Redis 5種資料結構 及使用場景分析Redis資料結構
- Unbound資料結構分析資料結構
- LinkedList 資料結構分析資料結構
- ArrayList 資料結構分析資料結構
- 資料結構程式碼常用模板資料結構
- 常用資料結構-namedtuple(命名元祖)資料結構
- 【PHP資料結構】PHP資料結構及演算法總結PHP資料結構演算法
- Java集合 HashSet的原理及常用方法Java
- SQL Server資料庫中表和索引結構儲存的原理及如何加快搜尋速度分析SQLServer資料庫索引
- 資料分析-pandas資料處理清洗常用總結
- 資料結構java版之氣泡排序及優化資料結構Java排序優化
- 列舉python常用的資料結構Python資料結構
- 常用的資料分析方法及案例講解
- 詳解Redis核心資料結構和高效能原理分析(一)Redis資料結構
- Java版-資料結構-棧Java資料結構
- JAVA資料結構之連結串列Java資料結構
- Java版-資料結構-連結串列Java資料結構
- HBase 系統架構及資料結構架構資料結構
- 資料結構及演算法資料結構演算法
- Java實現資料結構之線性結構Java資料結構
- Redis叢集模式和常用資料結構Redis模式資料結構
- 資料結構中常用的數學公式資料結構公式
- Java執行緒池原理及分析Java執行緒
- Java 資料結構總結 (未完成)Java資料結構