Java集合
Java集合框架:是一種工具類,就像是一個容器可以存儲任意數(shù)量的具有共同屬性的對象。
Java集合中成員很豐富,常用的集合有ArrayList,HashMap登馒,HashSet等。線程安全的有Vector馁痴,HashTable谊娇。線程不安全的有LinkedList,TreeMap,ArrayList济欢,HashMap等等赠堵。
集合中用到的數(shù)據(jù)結(jié)構(gòu)有以下幾種:
數(shù)組:最常用的數(shù)據(jù)結(jié)構(gòu)之一。數(shù)組的特點是長度固定法褥,可以用下標(biāo)索引茫叭,并且所有的元素的類型都是一致的。使用時盡量把數(shù)組封裝在一個類里半等,防止數(shù)據(jù)被錯誤的操作弄亂揍愁。
鏈表:是一種由多個節(jié)點組成的數(shù)據(jù)結(jié)構(gòu),并且每個節(jié)點包含有數(shù)據(jù)以及指向下一個節(jié)點的引用杀饵,在雙向鏈表里莽囤,還會有一個指向前一個節(jié)點的引用。例如切距,可以用單向鏈表和雙向鏈表來實現(xiàn)堆棧和隊列朽缎,因為鏈表的兩端都是可以進(jìn)行插入和刪除的動作的。當(dāng)然谜悟,也會有在鏈表的中間頻繁插入和刪除節(jié)點的場景话肖。
樹:是一種由節(jié)點組成的數(shù)據(jù)結(jié)構(gòu),每個節(jié)點都包含數(shù)據(jù)元素葡幸,并且有一個或多個子節(jié)點最筒,每個子節(jié)點指向一個父節(jié)點可以表示層級關(guān)系或者數(shù)據(jù)元素的順序關(guān)系。如果樹的每個子節(jié)點最多有兩個葉子節(jié)點蔚叨,那么這種樹被稱為二叉樹床蜘。二叉樹是一種非常常用的樹形結(jié)構(gòu), 因為它的這種結(jié)構(gòu)使得節(jié)點的插入和刪除都非常高效缅叠。樹的邊表示從一個節(jié)點到另外一個節(jié)點的快捷路徑悄泥。
堆棧:只允許對最后插入的元素進(jìn)行操作(也就是后進(jìn)先出虏冻,Last In First Out – LIFO)肤粱。如果你移除了棧頂?shù)脑兀敲茨憧梢圆僮鞯箶?shù)第二個元素厨相,依次類推领曼。這種后進(jìn)先出的方式是通過僅有的peek(),push()和pop()這幾個方法的強制性限制達(dá)到的。這種結(jié)構(gòu)在很多場景下都非常實用蛮穿,例如解析像(4+2)*3這樣的數(shù)學(xué)表達(dá)式庶骄,把源碼中的方法和異常按照他們出現(xiàn)的順序放到堆棧中,檢查你的代碼看看小括號和花括號是不是匹配的践磅,等等单刁。
隊列:和堆棧有些相似,不同之處在于在隊列里第一個插入的元素也是第一個被刪除的元素(即是先進(jìn)先出)府适。這種先進(jìn)先出的結(jié)構(gòu)是通過只提供peek()羔飞,offer()和poll()這幾個方法來訪問數(shù)據(jù)進(jìn)行限制來達(dá)到的肺樟。例如,排隊等待公交車逻淌,銀行或者超市里的等待列隊等等么伯,都是可以用隊列來表示。
Java集合框架圖
[圖片上傳失敗...(image-4b8b54-1530872801038)]
Collection interface
如上圖所示卡儒,Collection接口是最基本的集合接口田柔,它不提供直接的實現(xiàn),Java SDK提供的類都是繼承自Collection的“子接口”如List骨望,Set和Queue硬爆。Collection所代表的是一種規(guī)則,它所包含的元素都必須遵循一條或者多條規(guī)則擎鸠。如有些允許出現(xiàn)重復(fù)元素而有些則不允許重復(fù)摆屯、有些必須要按照順序插入而有些則是散列,有些支持排序但是有些則不支持等等糠亩。
List
List接口是Collection接口下的子接口虐骑。List所代表的是有序的Collection,即它用某種特定的插入順序來維護(hù)元素順序赎线。用戶可以對列表中每個元素的插入位置進(jìn)行精確地控制廷没,同時可以根據(jù)元素的整數(shù)索引(在列表中的位置)訪問元素,并搜索列表中的元素垂寥。實現(xiàn)List接口的集合主要有:ArrayList颠黎、LinkedList、Vector滞项、Stack狭归。
ArrayList
ArrayList基于數(shù)組實現(xiàn),可以通過下標(biāo)索引直接查找到指定位置的元素文判,因此查找效率高过椎,但每次插入或刪除元素,就要大量地移動元素戏仓,插入刪除元素的效率低疚宇。它允許任何符合規(guī)則的元素插入甚至包括null。每一個ArrayList都有一個初始容量(10)赏殃,該容量代表了數(shù)組的大小敷待。隨著容器中的元素不斷增加,容器的大小也會隨著增加仁热。在每次向容器中增加元素的同時都會進(jìn)行容量檢查榜揖,當(dāng)快溢出時,就會進(jìn)行擴容操作(擴容1.5倍)。所以如果我們明確所插入元素的多少举哟,最好指定一個初始容量值钳幅,避免過多的進(jìn)行擴容操作而浪費時間、效率炎滞。
ArrayList擅長于隨機訪問敢艰。同時ArrayList是非同步的,只能用在單線程環(huán)境下册赛,多線程環(huán)境下可以考慮用Collections.synchronizedList(List l)函數(shù)返回一個線程安全的ArrayList類钠导,也可以使用concurrent并發(fā)包下的CopyOnWriteArrayList類。
擴充容量的方法ensureCapacity森瘪。ArrayList在每次增加元素(可能是1個牡属,也可能是一組)時,都要調(diào)用該方法來確保足夠的容量扼睬。當(dāng)容量不足以容納當(dāng)前的元素個數(shù)時逮栅,就設(shè)置新的容量為舊的容量的1.5倍,如果設(shè)置后的新容量還不夠窗宇,則直接新容量設(shè)置為傳入的參數(shù)(也就是所需的容量)措伐,而后用Arrays.copyof()方法將元素拷貝到新的數(shù)組。從中可以看出军俊,當(dāng)容量不夠時侥加,每次增加元素,都要將原來的元素拷貝到一個新的數(shù)組中粪躬,非常之耗時担败,也因此建議在事先能確定元素數(shù)量的情況下,才使用ArrayList镰官,否則建議使用LinkedList提前。
LinkedList
LinkedList同樣實現(xiàn)List接口,與ArrayList不同的是泳唠,LinkedList是基于雙向鏈表實現(xiàn)的狈网,可以在任何位置進(jìn)行高效地插入和移除操作。但是LinkedList不能隨機訪問警检,它所有的操作都是要按照雙重鏈表的需要執(zhí)行孙援。在列表中索引的操作將從開頭或結(jié)尾遍歷列表(從靠近指定索引的一端)。這樣做的好處就是可以通過較低的代價在List中進(jìn)行插入和刪除操作扇雕。
與ArrayList一樣,LinkedList也是非同步的窥摄。如果多個線程同時訪問一個List镶奉,則必須自己實現(xiàn)訪問同步。一種解決方法是在創(chuàng)建List時構(gòu)造一個同步的List:
List list = Collections.synchronizedList(new LinkedList(…));
Vector
與ArrayList相似,但是Vector是同步的哨苛。所以說Vector是線程安全的動態(tài)數(shù)組鸽凶。它的操作與ArrayList幾乎一樣。
Stack
Stack繼承自Vector建峭,實現(xiàn)一個后進(jìn)先出的堆棧玻侥。Stack提供5個額外的方法使得Vector得以被當(dāng)作堆棧使用∫谡簦基本的push和pop 方法凑兰,還有peek方法得到棧頂?shù)脑兀琫mpty方法測試堆棧是否為空边锁,search方法檢測一個元素在堆棧中的位置姑食。Stack剛創(chuàng)建后是空棧。
Set
Set接口繼承了Collection接口茅坛。Set集合中不能包含重復(fù)的元素音半,每個元素必須是唯一的。你只需將元素加入set中贡蓖,重復(fù)的元素會自動移除曹鸠。有三種常見的Set實現(xiàn)——HashSet, TreeSet和LinkedHashSet。如果你需要一個訪問快速的Set斥铺,你應(yīng)該使用HashSet物延;當(dāng)你需要一個排序的Set,你應(yīng)該使用TreeSet仅父;當(dāng)你需要記錄下插入時的順序時叛薯,你應(yīng)該使用LinedHashSet。
HashSet
HashSet是是基于 HashMap 實現(xiàn)的笙纤,底層采用 HashMap 來保存元素,所以它不保證set 的迭代順序耗溜;特別是它不保證該順序恒久不變。add()省容、remove()以及contains()等方法都是復(fù)雜度為O(1)的方法抖拴。由于HashMap中key不可重復(fù),所以HashSet元素不可重復(fù)腥椒“⒄可以存儲null元素,是線程不安全的笼蛛。
TreeSet
TreeSet是一個有序集洒放,基于TreeMap實現(xiàn),是線程不安全的滨砍。
TreeSet底層采用TreeMap存儲往湿,構(gòu)造器啟動時新建TreeMap妖异。TreeSet存儲元素實際為TreeMap存儲的鍵值對為<key,PRESENT>的key;,PRESENT為固定對象:private static final Object PRESENT = new Object().
TreeSet支持兩種兩種排序方式领追,通過不同構(gòu)造器調(diào)用實現(xiàn)
自然排序:
public TreeSet() {
// 新建TreeMap他膳,自然排序
this(new TreeMap<E,Object>());
}
Comparator排序:
public TreeSet(Comparator<? super E> comparator) {
// 新建TreeMap,傳入自定義比較器comparator
this(new TreeMap<>(comparator));
}
TreeSet支持正向/反向迭代器遍歷和foreach遍歷
// 順序TreeSet:迭代器實現(xiàn)
Iterator iter = set.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
// 順序遍歷TreeSet:foreach實現(xiàn)
for (Integer i : set) {
System.out.println(i);
}
// 逆序遍歷TreeSet:反向迭代器實現(xiàn)
Iterator iter1 = set.descendingIterator();
while (iter1.hasNext()) {
System.out.println(iter1.next());
}
LinkedHashSet
LinkedHashSet介于HashSet和TreeSet之間绒窑。哈希表和鏈接列表實現(xiàn)棕孙。基本方法的復(fù)雜度為O(1)些膨。
LinkedHashSet 是 Set 的一個具體實現(xiàn)蟀俊,其維護(hù)著一個運行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序傀蓉,該迭代順序可為插入順序或是訪問順序欧漱。
LinkedHashSet 繼承于 HashSet,并且其內(nèi)部是通過 LinkedHashMap 來實現(xiàn)的葬燎。有點類似于我們之前說的LinkedHashMap 其內(nèi)部是基于 Hashmap 實現(xiàn)的一樣误甚。
如果我們需要迭代的順序為插入順序或者訪問順序,那么 LinkedHashSet 是需要你首先考慮的谱净。
LinkedHashSet 底層使用 LinkedHashMap 來保存所有元素窑邦,因為繼承于 HashSet,所有的方法操作上又與 HashSet 相同壕探,因此 LinkedHashSet 的實現(xiàn)上非常簡單冈钦,只提供了四個構(gòu)造方法,并通過傳遞一個標(biāo)識參數(shù)李请,調(diào)用父類的構(gòu)造器瞧筛,底層構(gòu)造一個 LinkedHashMap 來實現(xiàn),在相關(guān)操作上與父類 HashSet 的操作相同导盅,直接調(diào)用父類 HashSet 的方法即可较幌。
package java.util;
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
private static final long serialVersionUID = -2851667679971038690L;
/**
* 構(gòu)造一個帶有指定初始容量和加載因子的空鏈表哈希set。
*
* 底層會調(diào)用父類的構(gòu)造方法白翻,構(gòu)造一個有指定初始容量和加載因子的LinkedHashMap實例乍炉。
* @param initialCapacity 初始容量。
* @param loadFactor 加載因子滤馍。
*/
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
/**
* 構(gòu)造一個指定初始容量和默認(rèn)加載因子0.75的新鏈表哈希set岛琼。
*
* 底層會調(diào)用父類的構(gòu)造方法,構(gòu)造一個指定初始容量和默認(rèn)加載因子0.75的LinkedHashMap實例巢株。
* @param initialCapacity 初始容量槐瑞。
*/
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
/**
* 構(gòu)造一個默認(rèn)初始容量16和加載因子0.75的新鏈表哈希set。
*
* 底層會調(diào)用父類的構(gòu)造方法纯续,構(gòu)造一個默認(rèn)初始容量16和加載因子0.75的LinkedHashMap實例随珠。
*/
public LinkedHashSet() {
super(16, .75f, true);
}
/**
* 構(gòu)造一個與指定collection中的元素相同的新鏈表哈希set灭袁。
*
* 底層會調(diào)用父類的構(gòu)造方法猬错,構(gòu)造一個足以包含指定collection
* 中所有元素的初始容量和加載因子為0.75的LinkedHashMap實例窗看。
* @param c 其中的元素將存放在此set中的collection。
*/
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
}
}
通過觀察HashMap的源碼我們可以發(fā)現(xiàn):
Hash Map的前三個構(gòu)造函數(shù)倦炒,即訪問權(quán)限為public類型的構(gòu)造函數(shù)均是以HashMap作為實現(xiàn)显沈。而以LinkedHashMap作為實現(xiàn)的構(gòu)造函數(shù)的訪問權(quán)限是默認(rèn)訪問權(quán)限,即包內(nèi)訪問權(quán)限逢唤。
即:在java編程中拉讯,通過new創(chuàng)建的HashSet對象均是以HashMap作為實現(xiàn)基礎(chǔ)。只有在jdk中java.util包內(nèi)的源代碼才可能創(chuàng)建以LinkedHashMap作為實現(xiàn)的HashSet(LinkedHashSet就是通過封裝一個以LinkedHashMap為實現(xiàn)的HashSet來實現(xiàn)的)鳖藕。
只有包含三個參數(shù)的構(gòu)造函數(shù)才是采用的LinkedHashMap作為實現(xiàn)魔慷。
Map
Map與List、Set接口不同著恩,它是由一系列鍵值對組成的集合院尔,提供了key到Value的映射。同時它也沒有繼承Collection喉誊。在Map中它保證了key與value之間的一一對應(yīng)關(guān)系邀摆。也就是說一個key對應(yīng)一個value,所以它不能存在相同的key值伍茄,當(dāng)然value值可以相同栋盹。key可以為空,但是只允許出現(xiàn)一個null敷矫。它的主要實現(xiàn)類有HashMap例获、HashTable、LinkedHashMap曹仗、TreeMap榨汤。
HashMap
HashMap 是 Map 的一個實現(xiàn)類,它代表的是一種鍵值對的數(shù)據(jù)存儲形式整葡。
大多數(shù)情況下可以直接定位到它的值件余,因而具有很快的訪問速度,但遍歷順序卻是不確定的遭居。
HashMap最多只允許一條記錄的鍵為null啼器,允許多條記錄的值為null。遇到key為null的時候俱萍,調(diào)用putForNullKey方法進(jìn)行處理端壳,而對value沒有處理。不保證有序(比如插入的順序)枪蘑、也不保證序不隨時間變化损谦。
jdk 8 之前岖免,其內(nèi)部是由數(shù)組+鏈表來實現(xiàn)的,而 jdk 8 對于鏈表長度超過 8 的鏈表將轉(zhuǎn)儲為紅黑樹照捡。
HashMap非線程安全颅湘,即任一時刻可以有多個線程同時寫HashMap,可能會導(dǎo)致數(shù)據(jù)的不一致栗精。如果需要滿足線程安全闯参,可以用 Collections的synchronizedMap方法使HashMap具有線程安全的能力,或者使用ConcurrentHashMap悲立。
hash數(shù)組的默認(rèn)大小是16鹿寨,而且大小一定是2的指數(shù)
HashTable
Hashtable和HashMap一樣也是散列表,存儲元素也是鍵值對薪夕,底層實現(xiàn)是一個Entry數(shù)組+鏈表脚草。Hashtable繼承于Dictionary類(Dictionary類聲明了操作鍵值對的接口方法),實現(xiàn)Map接口(定義鍵值對接口)原献。HashTable是線程安全的馏慨,它的大部分類都被synchronized關(guān)鍵字修飾。key和value都不可為null嚼贡。
hash數(shù)組默認(rèn)大小是11熏纯,擴充方式是old*2+1
LinkedHashMap
LinkedHashMap繼承自HashMap實現(xiàn)了Map接口≡敛撸基本實現(xiàn)同HashMap一樣(底層基于數(shù)組+鏈表+紅黑樹實現(xiàn))樟澜,不同之處在于LinkedHashMap保證了迭代的有序性。其內(nèi)部維護(hù)了一個雙向鏈表叮盘,解決了 HashMap不能隨時保持遍歷順序和插入順序一致的問題秩贰。
除此之外,LinkedHashMap對訪問順序也提供了相關(guān)支持柔吼。在一些場景下毒费,該特性很有用,比如緩存愈魏。
在實現(xiàn)上觅玻,LinkedHashMap很多方法直接繼承自HashMap,僅為維護(hù)雙向鏈表覆寫了部分方法培漏。
默認(rèn)情況下溪厘,LinkedHashMap的迭代順序是按照插入節(jié)點的順序。也可以通過改變accessOrder參數(shù)的值牌柄,使得其遍歷順序按照訪問順序輸出畸悬。
TreeMap
TreeMap繼承自AbstractMap抽象類,并實現(xiàn)了SortedMap接口珊佣,如下圖所示:
[圖片上傳失敗...(image-fd7a40-1530872801038)]
TreeMap集合是基于紅黑樹(Red-Black tree)的 NavigableMap實現(xiàn)蹋宦。該集合最重要的特點就是可排序披粟,該映射根據(jù)其鍵的自然順序進(jìn)行排序,或者根據(jù)創(chuàng)建映射時提供的 Comparator 進(jìn)行排序冷冗,具體取決于使用的構(gòu)造方法守屉。
關(guān)于集合的常見問題
List和Map的區(qū)別
都是Java常用的容器,都是接口贾惦。不同的是List存儲的是單列的集合胸梆,Map存儲的是key-value鍵值對的集合敦捧。List中允許出現(xiàn)重復(fù)元素须板,Map中不允許key重復(fù)。List集合是有序的(儲存有序)蓬坡,Map集合是無序的(存儲無序)
Set中的元素不能重復(fù)鞍泉,如何實現(xiàn)肃晚?
Set大多都用的Map接口的實現(xiàn)類來實現(xiàn)的(HashSet基于HashMap實現(xiàn),TreeSet基于TreeMap實現(xiàn)甜奄,LinkedHashSet基于LinkedHashMap實現(xiàn))
在HashMap中通過如下實現(xiàn)來保證key值唯一
// 1. 如果key 相等
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 2. 修改對應(yīng)的value
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
添加元素的時候,如果key(也對應(yīng)的Set集合的元素)相等窃款,那么則修改value值课兄。而在Set集合中,value值僅僅是一個Object對象罷了(該對象對Set本身而言是無用的)晨继。
也就是說:Set集合如果添加的元素相同時烟阐,是根本沒有插入的(僅修改了一個無用的value值)。從源碼(HashMap)中也看出來紊扬,==和equals()方法都有使用蜒茄!
Vector和ArrayList
相同點:
這兩個類都實現(xiàn)了List接口,他們都是有序的集合(儲存有序)餐屎,底層都用數(shù)組實現(xiàn)檀葛。可以通過索引來獲取某個元素腹缩。允許元素重復(fù)和出現(xiàn)null值屿聋。ArrayList和Vector的迭代器實現(xiàn)都是fail-fast的。
不同點:
vector是線程同步的藏鹊,所以它也是線程安全的润讥,而arraylist是線程異步的,是不安全的伙判。如果不考慮到線程的安全因素象对,一般用arraylist效率比較高。
擴容時宴抚,arraylist擴容1.5倍勒魔,vector擴容2倍(或者擴容指定的大懈ι贰)
ArrayList 和Vector是采用數(shù)組方式存儲數(shù)據(jù),此數(shù)組元素數(shù)大于實際存儲的數(shù)據(jù)以便增加和插入元素冠绢,都允許直接序號索引元素抚吠,但是插入數(shù)據(jù)要設(shè)計到數(shù)組元素移動等內(nèi)存操作,所以索引數(shù)據(jù)快插入數(shù)據(jù)慢弟胀,Vector由于使用了synchronized方法(線程安全)所以性能上比ArrayList要差楷力,LinkedList使用雙向鏈表實現(xiàn)存儲,按序號索引數(shù)據(jù)需要進(jìn)行向前或向后遍歷孵户,但是插入數(shù)據(jù)時只需要記錄本項的前后項即可萧朝,所以插入數(shù)度較快!
Aarraylist和Linkedlist
ArrayList是基于數(shù)組實現(xiàn)的夏哭,LinkedList基于雙向鏈表實現(xiàn)的检柬。
ArrayList它支持以下標(biāo)位置進(jìn)行索引出對應(yīng)的元素(隨機訪問),而LinkedList則需要遍歷整個鏈表來獲取對應(yīng)的元素竖配。因此一般來說ArrayList的訪問速度是要比LinkedList要快的
ArrayList由于是數(shù)組何址,對于刪除和修改而言消耗是比較大(復(fù)制和移動數(shù)組實現(xiàn)),LinkedList是雙向鏈表刪除和修改只需要修改對應(yīng)的指針即可进胯,消耗是很小的用爪。因此一般來說LinkedList的增刪速度是要比ArrayList要快的
LinkedList比ArrayList消耗更多的內(nèi)存,因為LinkedList中的每個節(jié)點存儲了前后節(jié)點的引用胁镐。
對于增加/刪除元素操作
如果增刪都是在末尾來操作(每次調(diào)用的都是remove()和add())偎血,此時ArrayList就不需要移動和復(fù)制數(shù)組來進(jìn)行操作了。如果數(shù)據(jù)量有百萬級的時希停,速度是會比LinkedList要快的烁巫。
如果刪除操作的位置是在中間。由于LinkedList的消耗主要是在遍歷上宠能,ArrayList的消耗主要是在移動和復(fù)制上(底層調(diào)用的是arraycopy()方法亚隙,是native方法)。
LinkedList的遍歷速度是要慢于ArrayList的復(fù)制移動速度的
如果數(shù)據(jù)量有百萬級的時违崇,還是ArrayList要快阿弃。
哪些集合類提供對元素的隨機訪問?
ArrayList羞延、HashMap渣淳、TreeMap和HashTable類提供對元素的隨機訪問。
Enumeration和Iterator接口的區(qū)別
Enumeration的速度是Iterator的兩倍伴箩,也使用更少的內(nèi)存入愧。Enumeration是非常基礎(chǔ)的,也滿足了基礎(chǔ)的需要棺蛛。但是怔蚌,與Enumeration相比,Iterator更加安全旁赊,因為當(dāng)一個集合正在被遍歷的時候桦踊,它會阻止其它線程去修改集合。
Iterator的方法名比Enumeration更科學(xué)
Iterator有fail-fast機制终畅,比Enumeration更安全
Iterator能夠刪除元素籍胯,Enumeration并不能刪除元素
Iterater和ListIterator之間有什么區(qū)別?
我們可以使用Iterator來遍歷Set和List集合离福,而ListIterator只能遍歷List杖狼。
Iterator只可以向前遍歷,而LIstIterator可以雙向遍歷术徊。
ListIterator從Iterator接口繼承本刽,然后添加了一些額外的功能,比如添加一個元素赠涮、替換一個元素、獲取前面或后面元素的索引位置暗挑。
Java中HashMap的key值要是為類對象則該類需要滿足什么條件笋除?
需要同時重寫該類的hashCode()方法和它的equals()方法。
從源碼可以得知炸裆,在插入元素的時候是先算出該對象的hashCode垃它。如果hashcode相等話的。那么表明該對象是存儲在同一個位置上的烹看。
如果調(diào)用equals()方法国拇,兩個key相同,則替換元素
如果調(diào)用equals()方法惯殊,兩個key不相同酱吝,則說明該hashCode僅僅是碰巧相同,此時是散列沖突土思,將新增的元素放在桶子上
重寫了equals()方法务热,就要重寫hashCode()的方法。因為equals()認(rèn)定了這兩個對象相同己儒,而同一個對象調(diào)用hashCode()方法時崎岂,是應(yīng)該返回相同的值的!
HashSet與HashMap
HashSet 實現(xiàn)了 Set 接口闪湾,它不允許集合中有重復(fù)的值冲甘,當(dāng)我們提到 HashSet 時,第一件事情就是在將對象存儲在 HashSet 之前,要先確保對象重寫 equals()和 hashCode()方法江醇,這樣才能比較對象的值是否相等省艳,以確保set中沒有儲存相等的對象。如果我們沒有重寫這兩個方法嫁审,將會使用這個方法的默認(rèn)實現(xiàn)跋炕。
public boolean add(Object o)方法用來在 Set 中添加元素,當(dāng)元素值重復(fù)時則會立即返回 false律适,如果成功添加的話會返回 true辐烂。
HashMap 實現(xiàn)了 Map 接口,Map 接口對鍵值對進(jìn)行映射捂贿。Map 中不允許重復(fù)的鍵纠修。Map 接口有兩個基本的實現(xiàn),HashMap 和 TreeMap厂僧。TreeMap 保存了對象的排列次序扣草,而 HashMap 則不能。HashMap 允許鍵和值為 null颜屠。HashMap 是非 synchronized 的辰妙,但 collection 框架提供方法能保證 HashMap synchronized,這樣多個線程同時訪問 HashMap 時甫窟,能保證只有一個線程更改 Map密浑。
public Object put(Object Key,Object value)方法用來將元素添加到 map 中。
HashMap | HashSet |
---|---|
HashMap實現(xiàn)了Map接口 | HashSet實現(xiàn)了Set接口 |
HashMap儲存鍵值對 | HashSet僅僅存儲對象 |
使用put()方法將元素放入map中 | 使用add()方法將元素放入set中 |
HashMap中使用鍵對象來計算hashcode值 | HashSet使用成員對象來計算hashcode值粗井,對于兩個對象來說hashcode可能相同尔破,所以equals()方法用來判斷對象的相等性,如果兩個對象不同的話浇衬,那么返回false |
hashtable與hashmap
相同點:
儲存結(jié)構(gòu)和實現(xiàn)基本相同懒构,都是是實現(xiàn)的Map接口
不同點:
HashTable是同步的,HashMap是非同步的耘擂,需要同步的時候可以ConcurrentHashMap方法
HashMap允許為null胆剧,HashTable不允許為null
繼承不同,HashMap繼承的是AbstractMap梳星,HashTable繼承的是Dictionary
HashMap提供對key的Set進(jìn)行遍歷赞赖,因此它是fail-fast的,但HashTable提供對key的Enumeration進(jìn)行遍歷冤灾,它不支持fail-fast前域。
HashTable是一個遺留類,如果需要保證線程安全推薦使用CocurrentHashMap
HashMap與TreeMap
HashMap通過hashcode對其內(nèi)容進(jìn)行快速查找韵吨,而TreeMap中所有的元素都保持著某種固定的順序匿垄,如果你需要得到一個有序的結(jié)果你就應(yīng)該使用TreeMap(HashMap中元素的排列順序是不固定的)。HashMap中元素的排列順序是不固定的)。
在Map 中插入椿疗、刪除和定位元素漏峰,HashMap 是最好的選擇。但如果您要按自然順序或自定義順序遍歷鍵届榄,那么TreeMap會更好浅乔。使用HashMap要求添加的鍵類明確定義了hashCode()和 equals()的實現(xiàn)。 這個TreeMap沒有調(diào)優(yōu)選項铝条,因為該樹總處于平衡狀態(tài)靖苇。
集合框架中的泛型有什么優(yōu)點?
Java1.5引入了泛型班缰,所有的集合接口和實現(xiàn)都大量地使用它贤壁。泛型允許我們?yōu)榧咸峁┮粋€可以容納的對象類型,因此埠忘,如果你添加其它類型的任何元素脾拆,它會在編譯時報錯。這避免了在運行時出現(xiàn)ClassCastException莹妒,因為你將會在編譯時得到報錯信息名船。泛型也使得代碼整潔,我們不需要使用顯式轉(zhuǎn)換和instanceOf操作符动羽。它也給運行時帶來好處包帚,因為不會產(chǎn)生類型檢查的字節(jié)碼指令。
comparable 和 comparator的不同之處运吓?
comparable接口實際上是出自java.lang包
它有一個 compareTo(Object obj)方法來將objects排序
comparator接口實際上是出自 java.util 包
它有一個compare(Object obj1, Object obj2)方法來將objects排序
如何保證一個集合線程安全?
Vector, Hashtable, Properties 和 Stack 都是同步的類疯趟,所以它們都線程安全的拘哨,可以被使用在多線程環(huán)境中
使用Collections.synchronizedList(list)) 方法,可以保證list類是線程安全的
使用java.util.Collections.synchronizedSet()方法可以保證set類是線程安全的信峻。
TreeMap和TreeSet在排序時如何比較元素倦青?Collections工具類中的sort()方法如何比較元素?
TreeSet要求存放的對象所屬的類必須實現(xiàn)Comparable接口盹舞,該接口提供了比較元素的compareTo()方法产镐,當(dāng)插入元素時會回調(diào)該方法比較元素的大小。
TreeMap要求存放的鍵值對映射的鍵必須實現(xiàn)Comparable接口從而根據(jù)鍵對元素進(jìn)行排序踢步。
Collections工具類的sort方法有兩種重載的形式癣亚,第一種要求傳入的待排序容器中存放的對象比較實現(xiàn)Comparable接口以實現(xiàn)元素的比較;第二種不強制性的要求容器中的元素必須可比較获印,但是要求傳入第二個參數(shù)述雾,參數(shù)是Comparator接口的子類型(需要重寫compare方法實現(xiàn)元素的比較),相當(dāng)于一個臨時定義的排序規(guī)則,其實就是通過接口注入比較元素大小的算法玻孟,也是對回調(diào)模式的應(yīng)用(Java中對函數(shù)式編程的支持)唆缴。
什么是Java優(yōu)先級隊列?
Java PriorityQueue是一個數(shù)據(jù)結(jié)構(gòu)黍翎,它是Java集合框架的一部分面徽。 它是一個隊列的實現(xiàn),其中元素的順序?qū)⒏鶕?jù)每個元素的優(yōu)先級來決定匣掸。 實例化PriorityQueue時趟紊,可以在構(gòu)造函數(shù)中提供比較器。 該比較器將決定PriorityQueue集合實例中元素的排序順序旺聚。
Java hashCode()和equals()方法织阳。
equals()方法用于確定兩個Java對象的相等性。 當(dāng)我們有一個自定義類時砰粹,我們需要重寫equals()方法并提供一個實現(xiàn)唧躲,以便它可以用來找到它的兩個實例之間的相等性。 通過Java規(guī)范碱璃,equals()和hashCode()之間有一個契約弄痹。 它說,“如果兩個對象相等嵌器,即obj1.equals(obj2)為true肛真,那么obj1.hashCode()和obj2.hashCode()必須返回相同的整數(shù)”
無論何時我們選擇重寫equals(),我們都必須重寫hashCode()方法爽航。 hashCode()用于計算位置存儲區(qū)和key蚓让。