一沦童、ArrayList 和 Vector 的區(qū)別
這兩個(gè)類都實(shí)現(xiàn)了 List 接口(List 接口繼承了 Collection 接口)同波,他們都是有序集
合剃诅,即存儲(chǔ)在這兩個(gè)集合中的元素的位置都是有順序的,相當(dāng)于一種動(dòng)態(tài)的數(shù)組锦溪,我
們以后可以按位置索引號(hào)取出某個(gè)元素不脯,并且其中的數(shù)據(jù)是允許重復(fù)的府怯,這是
HashSet 之類的集合的最大不同處刻诊,HashSet 之類的集合不可以按索引號(hào)去檢索其
中的元素,也不允許有重復(fù)的元素(本來題目問的與 hashset 沒有任何關(guān)系牺丙,但為了
說清楚 ArrayList 與 Vector 的功能则涯,我們使用對(duì)比方式,更有利于說明問題)冲簿。接
著才說 ArrayList 與 Vector 的區(qū)別粟判,這主要包括兩個(gè)方面。
? 同步性:
Vector 是線程安全的峦剔,也就是說是它的方法之間是線程同步的档礁,而 ArrayList 是線
程序不安全的,它的方法之間是線程不同步的吝沫。如果只有一個(gè)線程會(huì)訪問到集合呻澜,那
最好是使用 ArrayList,因?yàn)樗豢紤]線程安全惨险,效率會(huì)高些羹幸;如果有多個(gè)線程會(huì)訪問
到集合,那最好是使用 Vector辫愉,因?yàn)椴恍枰覀冏约涸偃タ紤]和編寫線程安全的代
碼栅受。
備注:對(duì)于 Vector&ArrayList、Hashtable&HashMap,要記住線程安全的問題屏镊,
記住 Vector 與 Hashtable 是舊的依疼,是 java 一誕生就提供了的,它們是線程安全
的而芥,ArrayList 與 HashMap 是 java2 時(shí)才提供的涛贯,它們是線程不安全的。所以蔚出,我
們講課時(shí)先講老的弟翘。
? 數(shù)據(jù)增長:
ArrayList 與 Vector 都有一個(gè)初始的容量大小,當(dāng)存儲(chǔ)進(jìn)它們里面的元素的個(gè)數(shù)超
過了容量時(shí)骄酗,就需要增加 ArrayList 與 Vector 的存儲(chǔ)空間稀余,每次要增加存儲(chǔ)空間
時(shí),不是只增加一個(gè)存儲(chǔ)單元趋翻,而是增加多個(gè)存儲(chǔ)單元睛琳,每次增加的存儲(chǔ)單元的個(gè)數(shù)
在內(nèi)存空間利用與程序效率之間要取得一定的平衡。Vector 默認(rèn)增長為原來兩倍踏烙,而
ArrayList 的增長策略在文檔中沒有明確規(guī)定(從源代碼看到的是增長為原來的 1.5
倍)师骗。ArrayList 與 Vector 都可以設(shè)置初始的空間大小,Vector 還可以設(shè)置增長的
空間大小讨惩,而 ArrayList 沒有提供設(shè)置增長空間的方法辟癌。
總結(jié):即 Vector 增長原來的一倍,ArrayList 增加原來的 0.5 倍荐捻。
二黍少、說說 ArrayList,Vector, LinkedList 的存儲(chǔ)性能和特性
ArrayList 和 Vector 都是使用數(shù)組方式存儲(chǔ)數(shù)據(jù),此數(shù)組元素?cái)?shù)大于實(shí)際存儲(chǔ)的數(shù)
據(jù)以便增加和插入元素处面,它們都允許直接按序號(hào)索引元素厂置,但是插入元素要涉及數(shù)組
元素移動(dòng)等內(nèi)存操作,所以索引數(shù)據(jù)快而插入數(shù)據(jù)慢魂角,Vector 由于使用了
synchronized 方法(線程安全)昵济。
通常性能上較 ArrayList 差,而 LinkedList 使用雙向鏈表實(shí)現(xiàn)存儲(chǔ)野揪,按序號(hào)索引數(shù)
據(jù)需要進(jìn)行前向或后向遍歷访忿,但是插入數(shù)據(jù)時(shí)只需要記錄本項(xiàng)的前后項(xiàng)即可,所以插
入速度較快 囱挑。
ArrayList 在查找時(shí)速度快醉顽,LinkedList 在插入與刪除時(shí)更具優(yōu)勢(shì)。
三平挑、快速失敗 (fail-fast) 和安全失敗 (fail-safe) 的區(qū)別是什么游添?
Iterator 的安全失敗是基于對(duì)底層集合做拷貝系草,因此,它不受源集合上修改的影響唆涝。
java.util 包下面的所有的集合類都是快速失敗的找都,而 java.util.concurrent 包下面的
所有的類都是安全失敗的±群ǎ快速失敗的迭代器會(huì)拋出
ConcurrentModificationException 異常能耻,而安全失敗的迭代器永遠(yuǎn)不會(huì)拋出這樣的
異常。
四亡驰、hashmap 的數(shù)據(jù)結(jié)構(gòu)
在 java 編程語言中晓猛,最基本的結(jié)構(gòu)就是兩種,一個(gè)是數(shù)組凡辱,另外一個(gè)是模擬指針
(引用)戒职,所有的數(shù)據(jù)結(jié)構(gòu)都可以用這兩個(gè)基本結(jié)構(gòu)來構(gòu)造的,hashmap 也不例
外透乾。Hashmap 實(shí)際上是一個(gè)數(shù)組和鏈表的結(jié)合體(在數(shù)據(jù)結(jié)構(gòu)中洪燥,一般稱之為 “鏈
表散列 “)
enter image description here
五、HashMap 的工作原理是什么?
Java 中的 HashMap 是以鍵值對(duì) (key-value) 的形式存儲(chǔ)元素的乳乌。HashMap 需要
一個(gè) hash 函數(shù)捧韵,它使用 hashCode()和 equals()方法來向集合 / 從集合添加和檢
索元素。當(dāng)調(diào)用 put() 方法的時(shí)候汉操,HashMap 會(huì)計(jì)算 key 的 hash 值再来,然后把鍵
值對(duì)存儲(chǔ)在集合中合適的索引上。 如果 key 已經(jīng)存在了客情,value 會(huì)被更新成新值其弊。
HashMap 的一些重要的特性是它的容量 (capacity),負(fù)載因子 (load factor) 和擴(kuò)
容極限(threshold resizing)膀斋。
六、Hashmap 什么時(shí)候進(jìn)行擴(kuò)容呢痹雅?
當(dāng) hashmap 中的元素個(gè)數(shù)超過數(shù)組大小 loadFactor 時(shí)仰担,就會(huì)進(jìn)行數(shù)組擴(kuò)容,
loadFactor 的默認(rèn)值為 0.75绩社,也就是說摔蓝,默認(rèn)情況下,數(shù)組大小為 16愉耙,那么當(dāng)
hashmap 中元素個(gè)數(shù)超過 16 0.75=12 的時(shí)候贮尉,就把數(shù)組的大小擴(kuò)展為 2 16=32,
即擴(kuò)大一倍朴沿,然后重新計(jì)算每個(gè)元素在數(shù)組中的位置猜谚,而這是一個(gè)非常消耗性能的操
作败砂,所以如果我們已經(jīng)預(yù)知 hashmap 中元素的個(gè)數(shù),那么預(yù)設(shè)元素的個(gè)數(shù)能夠有效
的提高 hashmap 的性能魏铅。比如說昌犹,我們有 1000 個(gè)元素 new HashMap(1000),
但是理論上來講 new HashMap(1024) 更合適,不過上面 annegu 已經(jīng)說過览芳,即使
是 1000斜姥,hashmap 也自動(dòng)會(huì)將其設(shè)置為 1024。 但是 new HashMap(1024) 還
不是更合適的沧竟,因?yàn)?0.75*1000 < 1000, 也就是說為了讓 0.75 * size > 1000, 我們
必須這樣 new HashMap(2048) 才最合適铸敏,既考慮了 & 的問題,也避免了 resize
的問題悟泵。
七搞坝、List、Map魁袜、Set 三個(gè)接口桩撮,存取元素時(shí),各有什么特點(diǎn)峰弹?
這樣的題屬于隨意發(fā)揮題:這樣的題比較考水平店量,兩個(gè)方面的水平:一是要真正明白
這些內(nèi)容,二是要有較強(qiáng)的總結(jié)和表述能力鞠呈。如果你明白融师,但表述不清楚,在別人那
里則等同于不明白蚁吝。
首先旱爆,List 與 Set 具有相似性,它們都是單列元素的集合窘茁,所以怀伦,它們有一個(gè)功共同
的父接口,叫 Collection山林。Set 里面不允許有重復(fù)的元素房待,所謂重復(fù),即不能有兩個(gè)
相等(注意驼抹,不是僅僅是相同)的對(duì)象 桑孩,即假設(shè) Set 集合中有了一個(gè) A 對(duì)象,現(xiàn)在
我要向 Set 集合再存入一個(gè) B 對(duì)象框冀,但 B 對(duì)象與 A 對(duì)象 equals 相等流椒,則 B 對(duì)
象存儲(chǔ)不進(jìn)去,所以明也,Set 集合的 add 方法有一個(gè) boolean 的返回值宣虾,當(dāng)集合中
沒有某個(gè)元素惯裕,此時(shí) add 方法可成功加入該元素時(shí),則返回 true安岂,當(dāng)集合含有與某
個(gè)元素 equals 相等的元素時(shí)轻猖,此時(shí) add 方法無法加入該元素,返回結(jié)果為 false域那。
Set 取元素時(shí)咙边,沒法說取第幾個(gè),只能以 Iterator 接口取得所有的元素次员,再逐一遍歷
各個(gè)元素败许。
List 表示有先后順序的集合, 注意淑蔚,不是那種按年齡市殷、按大小、按價(jià)格之類的排序刹衫。
當(dāng)我們多次調(diào)用 add(Obj e) 方法時(shí)醋寝,每次加入的對(duì)象就像火車站買票有排隊(duì)順序一
樣,按先來后到的順序排序带迟。有時(shí)候音羞,也可以插隊(duì),即調(diào)用 add(int index,Obj e) 方
法仓犬,就可以指定當(dāng)前對(duì)象在集合中的存放位置嗅绰。一個(gè)對(duì)象可以被反復(fù)存儲(chǔ)進(jìn) List 中,
每調(diào)用一次 add 方法搀继,這個(gè)對(duì)象就被插入進(jìn)集合中一次窘面,其實(shí),并不是把這個(gè)對(duì)象
本身存儲(chǔ)進(jìn)了集合中叽躯,而是在集合中用一個(gè)索引變量指向這個(gè)對(duì)象财边,當(dāng)這個(gè)對(duì)象被
add 多次時(shí),即相當(dāng)于集合中有多個(gè)索引指向了這個(gè)對(duì)象险毁,如圖 x 所示制圈。List 除了
可以以 Iterator 接口取得所有的元素,再逐一遍歷各個(gè)元素之外畔况,還可以調(diào)用
get(index i) 來明確說明取第幾個(gè)。
Map 與 List 和 Set 不同慧库,它是雙列的集合跷跪,其中有 put 方法,定義如下:
put(obj key,obj value)齐板,每次存儲(chǔ)時(shí)吵瞻,要存儲(chǔ)一對(duì) key/value葛菇,不能存儲(chǔ)重復(fù)的
key,這個(gè)重復(fù)的規(guī)則也是按 equals 比較相等橡羞。取則可以根據(jù) key 獲得相應(yīng)的
value眯停,即 get(Object key) 返回值為 key 所對(duì)應(yīng)的 value。另外卿泽,也可以獲得所有
的 key 的結(jié)合莺债,還可以獲得所有的 value 的結(jié)合,還可以獲得 key 和 value 組合
成的 Map.Entry 對(duì)象的集合签夭。
List 以特定次序來持有元素齐邦,可有重復(fù)元素。Set 無法擁有重復(fù)元素, 內(nèi)部排序第租。
Map 保存 key-value 值措拇,value 可多值。
HashSet 按照 hashcode 值的某種運(yùn)算方式進(jìn)行存儲(chǔ)慎宾,而不是直接按 hashCode
值的大小進(jìn)行存儲(chǔ)丐吓。例如,"abc" ---> 78趟据,"def" ---> 62券犁,"xyz" ---> 65 在
hashSet 中的存儲(chǔ)順序不是 62,65,78,這些問題感謝以前一個(gè)叫崔健的學(xué)員提出之宿,
最后通過查看源代碼給他解釋清楚族操,看本次培訓(xùn)學(xué)員當(dāng)中有多少能看懂源碼。
LinkedHashSet 按插入的順序存儲(chǔ)比被,那被存儲(chǔ)對(duì)象的 hashcode 方法還有什么作用
呢色难?學(xué)員想想! hashset 集合比較兩個(gè)對(duì)象是否相等,首先看 hashcode 方法是否相
等等缀,然后看 equals 方法是否相等枷莉。new 兩個(gè) Student 插入到 HashSet 中,看
HashSet 的 size尺迂,實(shí)現(xiàn) hashcode 和 equals 方法后再看 size笤妙。
同一個(gè)對(duì)象可以在 Vector 中加入多次。往集合里面加元素噪裕,相當(dāng)于集合里用一根繩
子連接到了目標(biāo)對(duì)象蹲盘。往 HashSet 中卻加不了多次的。