常用集合類(Set、Map、List)比較
Java中資料儲存方式最底層的兩種結構:陣列和連結串列,陣列的特點:連續空間,定址迅速,但是在刪除或者新增元素的時候需要有較大幅度的移動,所以查詢速度快,增刪較慢。而連結串列正好相反,由於空間不連續,定址困難,增刪元素只需修改指標,所以查詢慢、增刪快。有沒有一種資料結構來綜合一下陣列和連結串列,以便發揮他們各自的優勢?答案是肯定的!
就是:雜湊表。雜湊表具有較快(常量級)的查詢速度,及相對較快的增刪速度,所以很適合在海量資料的環境中使用。一般實現雜湊表的方法採用“拉鍊法”。
部落格:https://blog.csdn.net/shadow_zed/article/details/78232955
1、HashSet、TreeSet
1、 儲存的資料結構不同: HashSet底層用的是HashMap雜湊表結構儲存,而TreeSet底層用二叉樹結構儲存。
2、儲存時保證資料唯一性依據不同:HashSet是通過複寫hashCode()方法和equals()方法來保證的,而TreeSet通過Compareable介面的compareTo()方法來保證的。
3、有序性不一樣:HashSet無序,可以放入null,但只能放入一個null,TreeSet有序且不允許放入null值,TreeSet是SortedSet介面的唯一實現類,TreeSet可以確保集合元素處於排序狀態。
public static void main(String[] args) { HashSet<String> set1 = new HashSet<String>(); set1.add("2"); set1.add("1"); set1.add("3"); set1.add(null); set1.add(null); System.out.println(set1);// [null, 3, 2, 1] TreeSet<String> set2 = new TreeSet<String>(); set2.add("2"); set2.add("1"); set2.add("3"); // set2.add(null); 執行報錯 System.out.println(set2);// [1, 2, 3] }
儲存原理:
1、HashSet:底層資料結構是雜湊表,本質就是對雜湊值的儲存,通過判斷元素的hashCode方法和equals方法來保證元素的唯一性,當hashCode值不相同,就直接儲存了,不用在判斷equals了,當hashCode值相同時,會在判斷一次euqals方法的返回值是否為true,如果為true則視為用一個元素,不用儲存,如果為false,這些相同雜湊值不同內容的元素都存放一個桶裡(當雜湊表中有一個桶結構,每一個桶都有一個雜湊值)
2、TreeSet:底層的資料結構是二叉樹,可以對Set集合中的元素進行排序,這種結構可以提高排序效能,根據比較方法的返回值確定的,只要返回的是0就代表元素重複。
插值:當向HashSet結合中存入一個元素時,HashSet會呼叫該物件的hashCode()方法來得到該物件的hashCode值,然後根據 hashCode值來決定該物件在HashSet中儲存位置。
簡單的說,HashSet集合判斷兩個元素相等的標準是兩個物件通過equals方法比較相等,並且兩個物件的hashCode()方法返回值相等。
TreeSet判斷兩個物件不相等的方式是兩個物件通過equals方法返回false,或者通過CompareTo方法比較沒有返回0。
LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的儲存位置,但是它同時使用連結串列維護元素的次序。這樣使得元素看起 來像是以插入順 序儲存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的新增順序訪問集合的元素。
LinkedHashSet在迭代訪問Set中的全部元素時,效能比HashSet好,但是插入時效能稍微遜色於HashSet。
LinkedHashSet集合同樣是根據元素的hashCode值來決定元素的儲存位置,但是它同時使用連結串列維護元素的次序。這樣使得元素看起 來像是以插入順 序儲存的,也就是說,當遍歷該集合時候,LinkedHashSet將會以元素的新增順序訪問集合的元素。
LinkedHashSet在迭代訪問Set中的全部元素時,效能比HashSet好,但是插入時效能稍微遜色於HashSet。
public static void main(String[] args)
{
/**
* LinkedHashSet
* 底層是連結串列實現的,是set集合中唯一一個能保證怎麼存就怎麼取的集合物件
* 因為是HashSet的子類,所以也是保證元素唯一的,與HashSet的原理一樣.
* HashSet的子類,LinkedHashSet也是根據元素的hashCode值來決定
* 元素的儲存位置,但它同時使用連結串列維護元素的次序,這樣使得元素看起來是
* 以插入的順序儲存的。
*/
LinkedHashSet<String> lhs = new LinkedHashSet<String>();
lhs.add("a");
lhs.add("b");
lhs.add("a");
lhs.add("c");
lhs.add("a");
lhs.add("d");
System.out.println(lhs);//[a, b, c, d]
}
2、HashMap、TreeMap
大多數情況下,只要不涉及執行緒安全問題,Map基本都可以使用HashMap,不過HashMap的順序並不是HashMap放置的順序,也就是無序。HashMap的這一缺點往往會帶來困擾。
HashMap是基於雜湊表實現的,每一個元素是一個key-value對,其內部通過單鏈表解決衝突問題,容量不足(超過了閥值)時,同樣會自動增長。HashMap最多隻允許一條記錄的鍵為Null,允許多條記錄的值為 Null。TreeMap沒有調優選項,因為該樹總處於平衡狀態。
HashMap是非執行緒安全的,只是用於單執行緒環境下,多執行緒環境下可以採用concurrent併發包下的concurrentHashMap。
HashMap:基於雜湊表實現。使用HashMap要求新增的鍵類明確定義了hashCode()和equals()(可以重寫hashCode()和equals()),為了優化HashMap空間的使用,您可以調優初始容量和負載因子。
比較:
(1)實現 : TreeMap:SortMap介面,基於紅黑樹 HashMap:基於雜湊散列表實現
(2)儲存 : TreeMap:預設按鍵的升序排序 HashMap:隨機儲存
(3)遍歷 : TreeMap:Iterator遍歷是排序的 HashMap:Iterator遍歷是隨機的
(4)效能損耗 :TreeMap:插入、刪除 HashMap:基本無
(5)鍵值對 : TreeMap:鍵、值都不能為null HashMap:只允許鍵、值均為null
(6)安全 : TreeMap:非併發安全Map HashMap:非併發安全Map
(7)效率 : TreeMap:低 HashMap:高
HashMap通常比TreeMap快一點(樹和雜湊表的資料結構使然)
HashMap通過hashcode對其內容進行快速查詢,而 TreeMap中所有的元素都保持著某種固定的順序,如果你需要得到一個有序的結果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。
HashMap:適用於在Map中插入、刪除和定位元素。
Treemap:適用於按自然順序或自定義順序遍歷鍵(key)。
優秀部落格:
http://www.importnew.com/24822.html
HashMap、HashTable區別:
HashMap是非執行緒安全的,HashTable是執行緒安全的, 內部的方法基本都是synchronized。
2、HashMap的鍵和值都允許有null值存在,而HashTable則不行。
3、因為執行緒安全的問題,HashMap效率比HashTable的要高。
HashTable、ConcurrentHashMap區別:
ConcurrentHashMap是執行緒安全的HashMap的實現。
同樣是執行緒安全的類,它與HashTable在同步方面有什麼不同呢?
synchronized關鍵字加鎖的原理,其實是對物件加鎖,不論你是在方法前加synchronized還是語句塊前加,鎖住的都是物件整體,但是ConcurrentHashMap的同步機制和這個不同,它不是加synchronized關鍵字,而是基於lock操作的,這樣的目的是保證同步的時候,鎖住的不是整個物件。事實上,ConcurrentHashMap可以滿足concurrentLevel個執行緒併發無阻塞的操作集合物件。
HashMap實現原理:
HashMap即是採用了鏈地址法,也就是陣列+連結串列的方式。
HashMap的主幹是一個Entry陣列。Entry是HashMap的基本組成單元,每一個Entry包含一個key-value鍵值對。
實現原理:
https://blog.csdn.net/lyt_7cs1dn9/article/details/54925837
LinkedHashSet:
是基於HashSet實現的,在HashSet的基礎上,增加了時間和空間上的開銷維持了一個雙向連結串列的關係, 保證了元素迭代的順序。
關注點 |
結論 |
LinkedHashMap是否允許空 |
Key和Value都允許空 |
LinkedHashMap是否允許重複資料 |
Key重複會覆蓋、Value允許重複 |
LinkedHashMap是否有序 |
有序 |
LinkedHashMap是否執行緒安全 |
非執行緒安全 |
集合異同比較:https://blog.csdn.net/learningcoding/article/details/79983248
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.TreeMap;
import java.util.WeakHashMap;
public class Map
{
public static void main(String[] args)
{
HashMap<Integer, Integer> map1 = new HashMap<Integer, Integer>();
map1.put(3, 3);
map1.put(1, 1);
map1.put(6, 6);
map1.put(6, 6);
map1.put(2, 2);
map1.put(5, 5);
System.out.println(map1);// {1=1, 2=2, 3=3, 5=5, 6=6}
TreeMap<Integer, Integer> map2 = new TreeMap<Integer, Integer>();
map2.put(3, 3);
map2.put(1, 1);
map2.put(6, 6);
map2.put(6, 6);
map2.put(2, 2);
map2.put(5, 5);
System.out.println(map2);// {1=1, 2=2, 3=3, 5=5, 6=6}
LinkedHashMap<Integer, Integer> map3 = new LinkedHashMap<Integer, Integer>();
map3.put(3, 3);
map3.put(1, 1);
map3.put(6, 6);
map3.put(6, 6);
map3.put(2, 2);
map3.put(5, 5);
System.out.println(map3);// {3=3, 1=1, 6=6, 2=2, 5=5}
Hashtable<Integer, Integer> map4 = new Hashtable<Integer, Integer>();
map4.put(3, 3);
map4.put(1, 1);
map4.put(6, 6);
map4.put(6, 6);
map4.put(2, 2);
map4.put(5, 5);
System.out.println(map4);// {6=6, 5=5, 3=3, 2=2, 1=1}
WeakHashMap<Integer, Integer> map5 = new WeakHashMap<Integer, Integer>();
map5.put(3, 3);
map5.put(1, 1);
map5.put(6, 6);
map5.put(6, 6);
map5.put(2, 2);
map5.put(5, 5);
System.out.println(map5);// {6=6, 5=5, 3=3, 2=2, 1=1}
}
}
3、ArrayList、LinkedList、Vector
ArrayList和LinkedList是順序儲存結構(基於動態陣列的資料結構)和鏈式儲存結構(雙向連結串列的資料結構)的表在java語言中的實現 。
ArrayList提供了一種可增長陣列的實現, ArrayList內部使用陣列實現,優點是對於get和set操作呼叫花費常數時間,缺點是插入元素和刪除元素會付出昂貴的代價。因為這個操作會導致後面的元素都要發生變動,除非操作發生在集合的末端。
LinkedList是一個雙鏈表的結構,在設計這個這個表結構時,我們需要考慮提供3個類:1、MyLinkedList類本身,2、Node類,該類作為靜態的內部類出現,包含一個節點上的資料,以及到前一個節點和後一個節點的鏈。3、LinkedListIterator類,也是一個私有類,實現Iterator介面,提供next().hannext().remove()方法。
LinkedList比ArrayList更佔記憶體,因為LinkedList的節點除了儲存資料,還儲存了兩個引用,一個指向前一個元素,一個指向後一個元素。
ArrayList 插入,刪除資料慢
LinkedList, 插入,刪除資料快
ArrayList是順序結構,所以定位很快,指哪找哪。 就像電影院位置一樣,有了電影票,一下就找到位置了。
LinkedList 是連結串列結構,就像手裡的一串佛珠,要找出第99個佛珠,必須得一個一個的數過去,所以定位慢。
Vector 和 ArrayList 一樣,都是繼承自 AbstractList。它是 Stack 的父類。英文的意思是 “向量”。
Vector 特點:1、底層由一個可以增長的陣列組成。2、Vector 通過 capacity (容量) 和 capacityIncrement (增長數量) 來儘量少的佔用空間。3、擴容時預設擴大兩倍。4、最好在插入大量元素前增加 vector 容量,那樣可以減少重新申請記憶體的次數。5、通過 iterator 和 lastIterator 獲得的迭代器是 fail-fast 的。5、通過 elements 獲得的老版迭代器 Enumeration 不是 fail-fast 的。6、同步類,每個方法前都有同步鎖 synchronized。
Vector ArrayList
共同點:
都是基於陣列
都支援隨機訪問
預設容量都是 10
都有擴容機制
區別:
Vector 出生的比較早,JDK 1.0 就出生了,ArrayList JDK 1.2 才出來
Vector 比 ArrayList 多一種迭代器 Enumeration
Vector 是執行緒安全的,ArrayList 不是
Vector 預設擴容 2 倍,ArrayList 是 1.5
都實現了List介面(List介面繼承了Collection介面)
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Stack;
public class ListTest
{
public static void main(String[] args)
{
ArrayList<Integer> list1 = new ArrayList<Integer>();
list1.add(3);
list1.add(1);
list1.add(2);
System.out.println(list1);// [3, 1, 2]
LinkedList<Integer> list2 = new LinkedList<Integer>();
list2.add(3);
list2.add(1);
list2.add(2);
System.out.println(list2);// [3, 1, 2]
/*
* Stack繼承於Vector,意味著Vector擁有的屬性和功能,Stack都擁有
*/
Stack<Integer> stack = new Stack<Integer>();
stack.push(2);// push將元素推入棧中
stack.push(1);
stack.push(3);
System.out.println(stack.peek());// 3 peek()取出棧頂元素,不執行刪除
stack.pop();// 取出棧頂元素,並將該元素從棧中刪除
System.out.println(stack.peek());// 1 peek()取出棧頂元素,不執行刪除
System.out.println(stack.isEmpty());// false 是否為空
}
}
HashCode方法
Java中的集合(Collection)有兩類,一類是List,再有一類是Set。前者集合內的元素是有序的,元素可以重複;後者元素無序,但元素不可重複。要想保證元素不重複,可兩個元素是否重複應該依據什麼來判斷呢?
這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後新增到集合中的元素比較的次數就非常多了。也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要呼叫1000次equals方法。這顯然會大大降低效率。
於是,Java採用了雜湊表的原理。雜湊演算法也稱為雜湊演算法,是將資料依特定演算法直接指定到一個地址上,hashCode方法實際上返回的就是物件儲存的實體地址(實際可能並不是)。這樣一來,當集合要新增新的元素時,先呼叫這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。 如果這個位置上沒有元素,它就可以直接儲存在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了, 就呼叫它的equals方法與新元素進行比較,相同的話就不存了,不相同就雜湊其它的地址。 所以這裡存在一個衝突解決的問題。這樣一來實際呼叫equals方法的次數就大大降低了,幾乎只需要一兩次。
Java對於eqauls方法和hashCode方法是這樣規定的:
1、如果兩個物件相同,那麼它們的hashCode值一定要相同;
2、如果兩個物件的hashCode相同,它們並不一定相同。
上面說的物件相同指的是用eqauls方法比較。
equals方法主要是用來判斷從表面上看或者從內容上看,2個物件是不是相等。
hashcode這個方法是用來鑑定2個物件是否相等的。hashcode方法一般使用者不會去呼叫。我們一般在覆蓋equals的同時也要 覆蓋hashcode,讓他們的邏輯一致。
要從物理上判斷2個物件是否相等,用==就可以了。
部落格:https://www.cnblogs.com/wl0000-03/p/6019627.html