List和Set繼承自Collection接口。
Set無序不允許元素重復。HashSet和TreeSet是兩個主要的實現類。
List有序且允許元素重復,支持null對象。ArrayList、LinkedList和Vector是三個主要的實現類。
Map也屬於集合系統,但和Collection接口沒關系。Map是key對value的映射集合,其中key列就是一個集合。key不能重復,但是value可以重復。HashMap、TreeMap和Hashtable是三個主要的實現類。
SortedSet和SortedMap接口對元素按指定規則排序,SortedMap是對key列進行排序。
ArrayList和vector區別
ArrayList和Vector都實現了List接口,都是通過數組實現的。
Vector是線程安全的,而ArrayList是非線程安全的。
List第一次創建的時候,會有一個初始大小,隨着不斷向List中增加元素,當List 認為容量不夠的時候就會進行擴容。Vector缺省情況下自動增長原來一倍的數組長度,ArrayList增長原來的50%。
ArrayList和LinkedList區別及使用場景
- 區別
ArrayList底層是用數組實現的,第一次add操作將數組的長度初始化為10,可以認為ArrayList是一個可改變大小的數組。隨着越來越多的元素被添加到ArrayList中,其規模是動態增加的,原理數組大小的1.5倍。
LinkedList底層是通過雙向鏈表實現的,每個節點Node對象包括指向上一個Node對象和指向下一個Node對象,同時LinkedList還實現了Queue接口,所以他還提供了offer(),peek(), poll()等方法。
LinkedList和ArrayList相比,增刪的速度較快。但是查詢和修改值的速度較慢。同時,
2.使用場景
LinkedList更適合從中間插入或者刪除(鏈表的特性)。
ArrayList更適合檢索和在末尾插入或刪除(數組的特性)。
HashTable實現原理
(1)是一個線程安全的散列表,存儲內容是鍵值對映射,不支持和null鍵值對
(2)繼承於Dictionary,實現了Map、Cloneable、java.io.Serializable接口
(3)線程安全,默認數組長度為11,負載因子為0.75,擴容方式是old*2+1;
具體原理參考文章:http://www.cnblogs.com/skywang12345/p/3310887.html
HashMap和HashTable區別
1).HashTable的方法前面都有synchronized來同步,是線程安全的;HashMap未經同步,是非線程安全的。
2).HashTable不允許null值(key和value都不可以) ;HashMap允許null值(key和value都可以)。
3).HashTable有一個contains(Object value)功能和containsValue(Object value)功能一樣。
4).HashTable使用Enumeration進行遍歷;HashMap使用Iterator進行遍歷。
5).HashTable中hash數組默認大小是11,增加的方式是old*2+1;HashMap中hash數組的默認大小是16,而且一定是2的指數。
6).哈希值的使用不同,HashTable直接使用對象的hashCode; HashMap重新計算hash值,而且用與代替求模。
HashMap實現原理
(1)基於Hash的map接口非同步實現, 無序且允許null的鍵值對。
(2)初始大小為16,默認負載因子0.75。當一個map填滿了75%的bucket時候,將會創建原來HashMap大小的2倍的bucket數組,來重新調整map的大小,並將原來的對象放入新的bucket數組中
(3) put(K key, V value)
根據key的hashCode值重新計算出hash值(高位計算一次散列,防止低位不變高位變化造成沖突),既而得到這個元素在數組中的位置(下標)如果數組該位置上已經存放有其他元素了,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。
(4) get(Object key)
計算key的hashCode,找到數組中對應位置的某一元素,然后通過key的equals方法在對應位置的鏈表中找到需要的元素。
(5) 重新調整HashMap大小存在什么問題嗎?
重新調整hashMap大小,確實存在競爭,多線程環境,調整大小的過程中,存儲在LinkedList中的元素的次序會反過來,因為移動到新的bucket位置的時候,HashMap並不會將元素放在LinkedList的尾部,而是放在頭部,這是為了避免尾部遍歷(tail traversing)。如果條件競爭發生了,那么就死循環了。
JDK7 與 JDK8 中關於HashMap的對比
結構不同:
JDK7 HashMap結構為 數組+鏈表 的形式。 JDK8 HashMap結構為 數組+鏈表+紅黑樹 的形式,當桶內元素大於8時,便會樹化。
hash值的計算方式不同
JDK7 table在創建hashmap時分配空間。 JDK8 在put的時候分配,如果table為空,則為table分配空間。
發生沖突時:
插入鏈表操,JDK7是頭插法,JDK8是尾插法。
resize操作:
JDK7 需要重新進行index的計算。 JDK8 不需要,通過判斷相應的位是0還是1,要么依舊是原index,要么是oldCap + 原index。
JDK7 與 JDK8 中關於ConcurrentHashMap的對比
結構不同:
JDK1.7 由Segment數組結構和HashEntry數組結構組成。Segment實際繼承自可重入鎖(ReentrantLock) JDK1.8 直接用Node數組+鏈表+紅黑樹的數據結構來實現,並發控制使用Synchronized和CAS來操作,整個看起來就像是優化過且線程安全的HashMap
鎖的粒度不同:
JDK1.7 版本鎖的粒度是基於Segment的,包含多個HashEntry,有上限 JDK1.8 的實現降低鎖的粒度,鎖的粒度就是HashEntry(首節點)
鎖的替代:
JDK1.8為什么使用內置鎖synchronized來代替重入鎖ReentrantLock
因為粒度降低了,在相對而言的低粒度加鎖方式,synchronized並不比ReentrantLock差,在粗粒度加鎖中ReentrantLock可能通過Condition來控制各個低粒度的邊界,更加的靈活,而在低粒度中,Condition的優勢就沒有了 基於JVM的synchronized優化空間更大,使用內嵌的關鍵字比使用API更加自然
size操作:
JDK7 先進行兩次無鎖統計,相同直接返回,不同再進行加鎖統計 JDK8 擴容和addCount()方法就已經有處理