1.Java
集合框架是什么?說出一些集合框架的優(yōu)點(diǎn)?
每種編程語言中都有集合,最初的Java
版本包含幾種集合類:Vector、Stack、HashTable
和Array
。隨著集合的廣泛使用,Java1.2
提出了囊括所有集合接口、實(shí)現(xiàn)和算法的集合框架。在保證線程安全的情況下使用泛型和并發(fā)集合類,Java
已經(jīng)經(jīng)歷了很久。它還包括在Java并發(fā)包中,阻塞接口以及它們的實(shí)現(xiàn)。集合框架的部分優(yōu)點(diǎn)如下:
- (1)使用核心集合類降低開發(fā)成本,而非實(shí)現(xiàn)我們自己的集合類。
- (2)隨著使用經(jīng)過嚴(yán)格測試的集合框架類,代碼質(zhì)量會(huì)得到提高。
- (3)通過使用
JDK
附帶的集合類,可以降低代碼維護(hù)成本。 - (4)復(fù)用性和可操作性。
2.集合框架中的泛型有什么優(yōu)點(diǎn)?
在Java1.5
引入了泛型,所有的集合接口和實(shí)現(xiàn)都大量地使用它。泛型允許我們?yōu)榧咸峁┮粋€(gè)可以容納的對象類型,因此,如果你添加其它類型的任何元素,它會(huì)在編譯時(shí)報(bào)錯(cuò)。這避免了在運(yùn)行時(shí)出現(xiàn)ClassCastException
,因?yàn)槟銓?huì)在編譯時(shí)得到報(bào)錯(cuò)信息。泛型也使得代碼整潔,我們不需要使用顯式轉(zhuǎn)換和instanceOf
操作符。它也給運(yùn)行時(shí)帶來好處,因?yàn)椴粫?huì)產(chǎn)生類型檢查的字節(jié)碼指令。也就是說雖然集合中泛型可以讓我們使用任何對象類型,但是只要我們選擇了一種類型則在后面代碼中就必須使用此類型,否則就會(huì)報(bào)錯(cuò)。
3.Java
集合框架的基礎(chǔ)接口有哪些?
Collection
為集合層級的根接口。一個(gè)集合代表一組對象,這些對象即為它的元素。Java
平臺(tái)不提供這個(gè)接口任何直接的實(shí)現(xiàn)。
Set
是一個(gè)不能包含重復(fù)元素的集合。這個(gè)接口對數(shù)學(xué)集合抽象進(jìn)行建模,被用來代表集合,就如一副牌。無序不可重復(fù)。
List
是一個(gè)有序集合,可以包含重復(fù)元素。你可以通過它的索引來訪問任何元素。List
更像長度動(dòng)態(tài)變換的數(shù)組。
Map
是一個(gè)將key
映射到value
的對象。一個(gè)Map
不能包含重復(fù)的key
:每個(gè)key
最多只能映射一個(gè)value
。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap
和ListIterator
。
4.為何Collection
不從Cloneable
和Serializable
接口繼承?
Collection
接口指定一組對象,對象即為它的元素。如何維護(hù)這些元素由Collection
的具體實(shí)現(xiàn)決定。例如,一些如List
的Collection
實(shí)現(xiàn)允許重復(fù)的元素,而其它的如Set
就不允許。很多Collection
實(shí)現(xiàn)有一個(gè)公有的clone
方法。然而,把它放到集合的所有實(shí)現(xiàn)中也是沒有意義的。這是因?yàn)?code>Collection是一個(gè)抽象表現(xiàn)。重要的是實(shí)現(xiàn)。
當(dāng)與具體實(shí)現(xiàn)打交道的時(shí)候,克隆或序列化的語義和含義才發(fā)揮作用。所以,具體實(shí)現(xiàn)應(yīng)該決定如何對它進(jìn)行克隆或序列化,或它是否可以被克隆或序列化。
在所有的實(shí)現(xiàn)中授權(quán)克隆和序列化,最終導(dǎo)致更少的靈活性和更多的限制。特定的實(shí)現(xiàn)應(yīng)該決定它是否可以被克隆和序列化。
5.為何Map
接口不繼承Collection
接口?
盡管Map
接口和它的實(shí)現(xiàn)也是集合框架的一部分,但Map
不是集合,集合也不是Map
。因此,Map
繼承Collection
毫無意義,反之亦然。
如果Map
繼承Collection
接口,那么元素去哪兒?Map
包含key-value
對,它提供抽取key
或value
列表集合的方法,但是它不適合“一組對象”規(guī)范。
6.Iterator
是什么?
Iterator
接口提供遍歷任何Collection
的接口。我們可以從一個(gè)Collection
中使用迭代器方法來獲取迭代器實(shí)例。迭代器取代了Java
集合框架中的Enumeration
。迭代器允許調(diào)用者在迭代過程中移除元素。
7.Enumeration
和Iterator
接口的區(qū)別?
Enumeration
的速度是Iterator
的兩倍,也使用更少的內(nèi)存。Enumeration
是非常基礎(chǔ)的,也滿足了基礎(chǔ)的需要。但是,與Enumeration
相比,Iterator
更加安全,因?yàn)楫?dāng)一個(gè)集合正在被遍歷的時(shí)候,它會(huì)阻止其它線程去修改集合。
迭代器取代了Java
集合框架中的Enumeration
。迭代器允許調(diào)用者從集合中移除元素,而Enumeration
不能做到。為了使它的功能更加清晰,迭代器方法名已經(jīng)經(jīng)過改善。
8.為何沒有像Iterator.add()
這樣的方法,向集合中添加元素?
邏輯上講,迭代時(shí)可以添加元素,但是一旦開放這個(gè)功能,很有可能造成很多意想不到的情況。 比如你在迭代一個(gè)ArrayList
,迭代器的工作方式是依次返回給你第0個(gè)元素,第1個(gè)元素,等等,假設(shè)當(dāng)你迭代到第5個(gè)元素的時(shí)候,你突然在ArrayList
的頭部插入了一個(gè)元素,使得你所有的元素都往后移動(dòng),于是你當(dāng)前訪問的第5個(gè)元素就會(huì)被重復(fù)訪問。 java
認(rèn)為在迭代過程中,容器應(yīng)當(dāng)保持不變。因此,java
容器中通常保留了一個(gè)域稱為modCount
,每次你對容器修改,這個(gè)值就會(huì)加1。當(dāng)你調(diào)用iterator
方法時(shí),返回的迭代器會(huì)記住當(dāng)前的modCount
,隨后迭代過程中會(huì)檢查這個(gè)值,一旦發(fā)現(xiàn)這個(gè)值發(fā)生變化,就說明你對容器做了修改,就會(huì)拋異常。
9.為何迭代器沒有一個(gè)方法可以直接獲取下一個(gè)元素,而不需要移動(dòng)游標(biāo)?
它可以在當(dāng)前Iterator
的頂層實(shí)現(xiàn),但是它用得很少,如果將它加到接口中,每個(gè)繼承都要去實(shí)現(xiàn)它,這沒有意義。
10.Iterater
和ListIterator
之間有什么區(qū)別?
(1)我們可以使用Iterater
來遍歷Set
和List
集合,而ListIterator
只能遍歷List
。
(2)Iterater
只可以向前遍歷,而ListIterator
可以雙向遍歷。
(3)ListIterator
從Iterater
接口繼承,然后添加了一些額外的功能,比如添加一個(gè)元素、替換一個(gè)元素、獲取前面或后面元素的索引位置。
11.遍歷一個(gè)List
有哪些不同的方式?
List<String> strList = new ArrayList<>();
//使用for-each循環(huán)
for(String obj : strList){
System.out.println(obj);
}
//using iterator
Iterator<String> it = strList.iterator();
while(it.hasNext()){
String obj = it.next();
System.out.println(obj);
}
使用迭代器更加線程安全,因?yàn)樗梢源_保,在當(dāng)前遍歷的集合元素被更改的時(shí)候,它會(huì)拋出ConcurrentModificationException
。
12.通過迭代器fail-fast
屬性,你明白了什么?
每次我們嘗試獲取下一個(gè)元素的時(shí)候,Iterator fail-fast
屬性檢查當(dāng)前集合結(jié)構(gòu)里的任何改動(dòng)。如果發(fā)現(xiàn)任何改動(dòng),它拋出ConcurrentModificationException
。Collection
中所有Iterator
的實(shí)現(xiàn)都是按fail-fast
來設(shè)計(jì)的(ConcurrentHashMap
和CopyOnWriteArrayList
這類并發(fā)集合類除外)。
13.fail-fast
與fail-safe
有什么區(qū)別?
Iterator
的fail-fast
屬性與當(dāng)前的集合共同起作用,因此它不會(huì)受到集合中任何改動(dòng)的影響。Java.util
包中的所有集合類都被設(shè)計(jì)為fail-fast
的,而java.util.concurrent
(并發(fā)工具包)中的集合類都為fail-safe
的。fail-fast
迭代器拋出ConcurrentModificationException
,而fail-safe
迭代器從不拋出ConcurrentModificationException
。
(并發(fā)工具包請參考:http://blog.csdn.net/defonds/article/details/44021605
)
14.在迭代一個(gè)集合的時(shí)候,如何避免ConcurrentModificationException
?
在遍歷一個(gè)集合的時(shí)候,我們可以使用并發(fā)集合類來避免ConcurrentModificationException
,比如使用CopyOnWriteArrayList
,而不是ArrayList
。
15.為何Iterator
接口沒有具體的實(shí)現(xiàn)?
Iterator
接口定義了遍歷集合的方法,但它的實(shí)現(xiàn)則是集合實(shí)現(xiàn)類的責(zé)任。每個(gè)能夠返回用于遍歷的Iterator
的集合類都有它自己的Iterator
實(shí)現(xiàn)內(nèi)部類。
這就允許集合類去選擇迭代器是fail-fast
還是fail-safe
的。比如,ArrayList
迭代器是fail-fast
的,而CopyOnWriteArrayList
迭代器是fail-safe
的。
16.UnsupportedOperationException
是什么?
UnsupportedOperationException
是用于表明操作不支持的異常。在JDK
類中已被大量運(yùn)用,在集合框架java.util.Collections.UnmodifiableCollection
將會(huì)在所有add
和remove
操作中拋出這個(gè)異常。
17.在Java
中,HashMap
是如何工作的?
HashMap
在Map.Entry
靜態(tài)內(nèi)部類實(shí)現(xiàn)中存儲(chǔ)key-value
對。HashMap
使用哈希算法,在put
和get
方法中,它使用hashCode()
和equals()
方法。當(dāng)我們通過傳遞key-value
對調(diào)用put
方法的時(shí)候,HashMap
使用Key hashCode()
和哈希算法來找出存儲(chǔ)key-value
對的索引。Entry
存儲(chǔ)在LinkedList
中,所以如果存在entry
,它使用equals()
方法來檢查傳遞的key
是否已經(jīng)存在,如果存在,它會(huì)覆蓋value
,如果不存在,它會(huì)創(chuàng)建一個(gè)新的entry
然后保存。當(dāng)我們通過傳遞key
調(diào)用get
方法時(shí),它再次使用hashCode()
來找到數(shù)組中的索引,然后使用equals()
方法找出正確的Entry
,然后返回它的值。下面的圖片解釋了詳細(xì)內(nèi)容。
其它關(guān)于HashMap
比較重要的問題是容量、負(fù)荷系數(shù)和閥值調(diào)整。HashMap
默認(rèn)的初始容量是32,負(fù)荷系數(shù)是0.75。閥值是為負(fù)荷系數(shù)乘以容量,無論何時(shí)我們嘗試添加一個(gè)entry
,如果map
的大小比閥值大的時(shí)候,HashMap
會(huì)對map
的內(nèi)容進(jìn)行重新哈希,且使用更大的容量。容量總是2的冪,所以如果你知道你需要存儲(chǔ)大量的key-value
對,比如緩存從數(shù)據(jù)庫里面拉取的數(shù)據(jù),使用正確的容量和負(fù)荷系數(shù)對HashMap
進(jìn)行初始化是個(gè)不錯(cuò)的做法。
18.hashCode()
和equals()
方法有何重要性?
HashMap
使用Key
對象的hashCode()
和equals()
方法去決定key-value
對的索引。當(dāng)我們試著從HashMap
中獲取值的時(shí)候,這些方法也會(huì)被用到。如果這些方法沒有被正確地實(shí)現(xiàn),在這種情況下,兩個(gè)不同Key
也許會(huì)產(chǎn)生相同的hashCode()
和equals()
輸出,HashMap
將會(huì)認(rèn)為它們是相同的,然后覆蓋它們,而非把它們存儲(chǔ)到不同的地方。同樣的,所有不允許存儲(chǔ)重復(fù)數(shù)據(jù)的集合類都使用hashCode()
和equals()
去查找重復(fù),所以正確實(shí)現(xiàn)它們非常重要。equals()
和hashCode()
的實(shí)現(xiàn)應(yīng)該遵循以下規(guī)則:
- (1)如果
o1.equals(o2)
,那么o1.hashCode() == o2.hashCode()
總是為true
的。 - (2)如果
o1.hashCode() == o2.hashCode()
,并不意味著o1.equals(o2)
會(huì)為true
。
19.我們能否使用任何類作為Map
的key
?
我們可以使用任何類作為Map
的key
,然而在使用它們之前,需要考慮以下幾點(diǎn):
- (1)如果類重寫了
equals()
方法,它也應(yīng)該重寫hashCode()
方法。 - (2)類的所有實(shí)例需要遵循與
equals()
和hashCode()
相關(guān)的規(guī)則。請參考之前提到的這些規(guī)則。 - (3)如果一個(gè)類沒有使用
equals()
,你不應(yīng)該在hashCode()
中使用它。 - (4)用戶自定義
key
類的最佳實(shí)踐是使之為不可變的,這樣,hashCode()
值可以被緩存起來,擁有更好的性能。不可變的類也可以確保hashCode()
和equals()
在未來不會(huì)改變,這樣就會(huì)解決與可變相關(guān)的問題了。
比如,我有一個(gè)類MyKey
,在HashMap
中使用它。
//傳遞給MyKey
的name
參數(shù)被用于equals()
和hashCode()
()中
MyKey key = new MyKey('Pankaj'); //assume hashCode=1234
myHashMap.put(key, 'Value');
// 以下的代碼會(huì)改變key
的hashCode()
和equals()
值
key.setName('Amit'); //assume new hashCode=7890
//下面會(huì)返回null
,因?yàn)?code>HashMap會(huì)嘗試查找存儲(chǔ)同樣索引的key
,而key
已被改變了,匹配失敗,返回null
myHashMap.get(new MyKey('Pankaj'))
那就是為何String
和Integer
被作為HashMap
的key
大量使用。
20.Map
接口提供了哪些不同的集合視圖?
Map
接口提供三個(gè)集合視圖:
(1)Set keyset()
:返回Map
中包含的所有key
的一個(gè)Set
視圖。集合是受Map
支持的,Map
的變化會(huì)在集合中反映出來,反之亦然。當(dāng)一個(gè)迭代器正在遍歷一個(gè)集合時(shí),若Map
被修改了(除迭代器自身的移除操作以外),迭代器的結(jié)果會(huì)變?yōu)槲炊x。集合支持通過Iterator
的Remove
、Set.remove
、removeAll
、retainAll
和clear
操作進(jìn)行元素移除,從map
中移除對應(yīng)的映射。它不支持add
和addAll
操作。
(2)Collection values()
:返回一個(gè)Map
中包含的所有value
的一個(gè)Collection
視圖。這個(gè)Collection
受Map
支持的,Map
的變化會(huì)在Collection
中反映出來,反之亦然。當(dāng)一個(gè)迭代器正在遍歷一個(gè)Collection
時(shí),若Map
被修改了(除迭代器自身的移除操作以外),迭代器的結(jié)果會(huì)變?yōu)槲炊x。集合支持通過Iterator
的Remove
、Set.remove
、removeAll
、retainAll
和clear
操作進(jìn)行元素移除,從Map
中移除對應(yīng)的映射。它不支持add
和addAll
操作。
(3)Set<Map.Entry<K,V>> entrySet()
:返回一個(gè)Map
鐘包含的所有映射的一個(gè)集合視圖。這個(gè)集合受Map
支持的,Map
的變化會(huì)在Collection
中反映出來,反之亦然。當(dāng)一個(gè)迭代器正在遍歷一個(gè)集合時(shí),若map
被修改了(除迭代器自身的移除操作,以及對迭代器返回的entry
進(jìn)行setValue
外),迭代器的結(jié)果會(huì)變?yōu)槲炊x。集合支持通過Iterator
的Remove
、Set.remove
、removeAll
、retainAll
和clear
操作進(jìn)行元素移除,從map
中移除對應(yīng)的映射。它不支持add
和addAll
操作。
21.HashMap
和HashTable
有何不同?
- (1)
HashMap
允許key
和value
為null
,而HashTable
不允許。 - (2)
HashTable
是同步的,而HashMap
不是。所以HashMap
適合單線程環(huán)境,HashTable
適合多線程環(huán)境。 - (3)在
Java1.4
中引入了LinkedHashMap
,HashMap
的一個(gè)子類,假如你想要遍歷順序,你很容易從HashMap
轉(zhuǎn)向LinkedHashMap
,但是HashTable
不是這樣的,它的順序是不可預(yù)知的。 - (4)
HashMap
提供對key
的Set
進(jìn)行遍歷,因此它是fail-fast
的,但HashTable
提供對key
的Enumeration
進(jìn)行遍歷,它不支持fail-fast
。 - (5)
HashTable
被認(rèn)為是個(gè)遺留的類,如果你尋求在迭代的時(shí)候修改Map
,你應(yīng)該使用CocurrentHashMap
。
22.如何決定選用HashMap
還是TreeMap
?
對于在Map
中插入、刪除和定位元素這類操作,HashMap
是最好的選擇。然而,假如你需要對一個(gè)有序的key集合進(jìn)行遍歷,TreeMap
是更好的選擇。基于你的collection
的大小,也許向HashMap
中添加元素會(huì)更快,將Map
換為TreeMap
進(jìn)行有序key
的遍歷。
23.ArrayList
和Vector
有何異同點(diǎn)?
ArrayList
和Vector
在很多時(shí)候都很類似。
(1)兩者都是基于索引的,內(nèi)部由一個(gè)數(shù)組支持。
(2)兩者維護(hù)插入的順序,我們可以根據(jù)插入順序來獲取元素。
(3)ArrayList
和Vector
的迭代器實(shí)現(xiàn)都是fail-fast
的。
(4)ArrayList
和Vector
兩者允許null
值,也可以使用索引值對元素進(jìn)行隨機(jī)訪問。
以下是ArrayList
和Vector
的不同點(diǎn):
(1)Vector
是同步的,而ArrayList
不是。然而,如果你尋求在迭代的時(shí)候?qū)α斜磉M(jìn)行改變,你應(yīng)該使用CopyOnWriteArrayList
。
(2)ArrayList
比Vector
快,它因?yàn)橛型剑粫?huì)過載。
(3)ArrayList
更加通用,因?yàn)槲覀兛梢允褂?code>Collections工具類輕易地獲取同步列表和只讀列表。
24.Array
和ArrayList
有何區(qū)別?什么時(shí)候更適合用Array
?
Array
可以容納基本類型和對象,而ArrayList
只能容納對象。
Array
是指定大小的,而ArrayList
大小是固定的。
Array
沒有提供ArrayList
那么多功能,比如addAll、removeAll
和iterator
等。盡管ArrayList
明顯是更好的選擇,但也有些時(shí)候Array
比較好用:
(1)如果列表的大小已經(jīng)指定,大部分情況下是存儲(chǔ)和遍歷它們。
(2)對于遍歷基本數(shù)據(jù)類型,盡管Collections
使用自動(dòng)裝箱來減輕編碼任務(wù),在指定大小的基本類型的列表上工作也會(huì)變得很慢。
(3)如果你要使用多維數(shù)組,使用[][]
比List<List<>>
更容易。
25.ArrayList
和LinkedList
有何區(qū)別?
ArrayList
和LinkedList
兩者都實(shí)現(xiàn)了List
接口,但是它們之間有些不同。
(1)ArrayList
是由Array
所支持的基于一個(gè)索引的數(shù)據(jù)結(jié)構(gòu),所以它提供對元素的隨機(jī)訪問,復(fù)雜度為O(1)
,但LinkedList
存儲(chǔ)一系列的節(jié)點(diǎn)數(shù)據(jù),每個(gè)節(jié)點(diǎn)都與前一個(gè)和下一個(gè)節(jié)點(diǎn)相連接。所以,盡管有使用索引獲取元素的方法,內(nèi)部實(shí)現(xiàn)是從起始點(diǎn)開始遍歷,遍歷到索引的節(jié)點(diǎn)然后返回元素,時(shí)間復(fù)雜度為O(n)
,比ArrayList
要慢。
(2)與ArrayList
相比,在LinkedList
中插入、添加和刪除一個(gè)元素會(huì)更快,因?yàn)樵谝粋€(gè)元素被插入到中間的時(shí)候,不會(huì)涉及改變數(shù)組的大小,或更新索引。
(3)LinkedList
比ArrayList
消耗更多的內(nèi)存,因?yàn)?code>LinkedList中的每個(gè)節(jié)點(diǎn)存儲(chǔ)了前后節(jié)點(diǎn)的引用。
26.哪些集合類提供對元素的隨機(jī)訪問?
ArrayList、HashMap、TreeMap
和HashTable
類提供對元素的隨機(jī)訪問。
27.EnumSet
是什么?
java.util.EnumSet
是使用枚舉類型的集合實(shí)現(xiàn)。當(dāng)集合創(chuàng)建時(shí),枚舉集合中的所有元素必須來自單個(gè)指定的枚舉類型,可以是顯示的或隱示的。EnumSet
是不同步的,不允許值為null
的元素。它也提供了一些有用的方法,比如copyOf(Collection c)、of(E first,E…rest)
和complementOf(EnumSet s)
。
28.哪些集合類是線程安全的?
Vector、HashTable、Properties
和Stack
是同步類,所以它們是線程安全的,可以在多線程環(huán)境下使用。Java1.5
并發(fā)API
包括一些集合類,允許迭代時(shí)修改,因?yàn)樗鼈兌脊ぷ髟诩系目寺∩希运鼈冊诙嗑€程環(huán)境中是安全的。
29.并發(fā)集合類是什么?
Java1.5
并發(fā)包(java.util.concurrent
)包含線程安全集合類,允許在迭代時(shí)修改集合。迭代器被設(shè)計(jì)為fail-fast
的,會(huì)拋出ConcurrentModificationException
。一部分類為:CopyOnWriteArrayList、 ConcurrentHashMap、CopyOnWriteArraySet
。
30.BlockingQueue
是什么?
Java.util.concurrent.BlockingQueue
是一個(gè)隊(duì)列,在進(jìn)行檢索或移除一個(gè)元素的時(shí)候,它會(huì)等待隊(duì)列變?yōu)榉强眨划?dāng)在添加一個(gè)元素時(shí),它會(huì)等待隊(duì)列中的可用空間。BlockingQueue
接口是Java
集合框架的一部分,主要用于實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式。我們不需要擔(dān)心等待生產(chǎn)者有可用的空間,或消費(fèi)者有可用的對象,因?yàn)樗荚?code>BlockingQueue的實(shí)現(xiàn)類中被處理了。Java
提供了集中BlockingQueue
的實(shí)現(xiàn),比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue
等。
31.隊(duì)列和棧是什么,列出它們的區(qū)別?
棧和隊(duì)列兩者都被用來預(yù)存儲(chǔ)數(shù)據(jù)。java.util.Queue
是一個(gè)接口,它的實(shí)現(xiàn)類在Java
并發(fā)包中。隊(duì)列允許先進(jìn)先出(FIFO
)檢索元素,但并非總是這樣。Deque
接口允許從兩端檢索元素。棧與隊(duì)列很相似,但它允許對元素進(jìn)行后進(jìn)先出(LIFO
)進(jìn)行檢索Stack
是一個(gè)擴(kuò)展自Vector
的類,而Queue
是一個(gè)接口。
32.Collections
類是什么?
Java.util.Collections
是一個(gè)工具類僅包含靜態(tài)方法,它們操作或返回集合。它包含操作集合的多態(tài)算法,返回一個(gè)由指定集合支持的新集合和其它一些內(nèi)容。這個(gè)類包含集合框架算法的方法,比如折半搜索、排序、混編和逆序等。
33.Comparable
和Comparator
接口是什么?
如果我們想使用Array
或Collection
的排序方法時(shí),需要在自定義類里實(shí)現(xiàn)Java
提供Comparable
接口。Comparable
接口有compareTo(T OBJ)
方法,它被排序方法所使用。我們應(yīng)該重寫這個(gè)方法,如果"this"
對象比傳遞的對象參數(shù)更小、相等或更大時(shí),它返回一個(gè)負(fù)整數(shù)、0或正整數(shù)。但是,在大多數(shù)實(shí)際情況下,我們想根據(jù)不同參數(shù)進(jìn)行排序。比如,作為一個(gè)CEO
,我想對雇員基于薪資進(jìn)行排序,一個(gè)HR
想基于年齡對他們進(jìn)行排序。這就是我們需要使用Comparator
接口的情景,因?yàn)?code>Comparable.compareTo(Object o)方法實(shí)現(xiàn)只能基于一個(gè)字段進(jìn)行排序,我們不能根據(jù)對象排序的需要選擇字段。Comparator
接口的compare(Object o1, Object o2)
方法的實(shí)現(xiàn)需要傳遞兩個(gè)對象參數(shù),若第一個(gè)參數(shù)比第二個(gè)小,返回負(fù)整數(shù);若第一個(gè)等于第二個(gè),返回0;若第一個(gè)比第二個(gè)大,返回正整數(shù)。
34.Comparable
和Comparator
接口有何區(qū)別?
Comparable
和Comparator
接口被用來對對象集合或者數(shù)組進(jìn)行排序。Comparable
接口被用來提供對象的自然排序,我們可以使用它來提供基于單個(gè)邏輯的排序。Comparator
接口被用來提供不同的排序算法,我們可以選擇需要使用的Comparator
來對給定的對象集合進(jìn)行排序。Comparable
是排序接口;若一個(gè)類實(shí)現(xiàn)了Comparable
接口,就意味著“該類支持排序”。而Comparator
是比較器;我們?nèi)粜枰刂颇硞€(gè)類的次序,可以建立一個(gè)“該類的比較器”來進(jìn)行排序。
35.我們?nèi)绾螌σ唤M對象進(jìn)行排序?
如果我們需要對一個(gè)對象數(shù)組進(jìn)行排序,我們可以使用Arrays.sort()
方法。如果我們需要排序一個(gè)對象列表,我們可以使用Collections.sort()
方法。兩個(gè)類都有用于自然排序(使用Comparable
)或基于標(biāo)準(zhǔn)的排序(使用Comparator
)的重載方法sort()
。Collections
內(nèi)部使用數(shù)組排序方法,所有它們兩者都有相同的性能,只是Collections
需要花時(shí)間將列表轉(zhuǎn)換為數(shù)組。
36.當(dāng)一個(gè)集合被作為參數(shù)傳遞給一個(gè)函數(shù)時(shí),如何才可以確保函數(shù)不能修改它?
在作為參數(shù)傳遞之前,我們可以使用Collections.unmodifiableCollection(Collection c)
方法創(chuàng)建一個(gè)只讀集合,這將確保改變集合的任何操作都會(huì)拋出UnsupportedOperationException
。
37.我們?nèi)绾螐慕o定集合那里創(chuàng)建一個(gè)synchronized
的集合?
我們可以使用Collections.synchronizedCollection(Collection c)
根據(jù)指定集合來獲取一個(gè)synchronized
(線程安全的)集合。
38.集合框架里實(shí)現(xiàn)的通用算法有哪些?
Java
集合框架提供常用的算法實(shí)現(xiàn),比如排序和搜索。Collections
類包含這些方法實(shí)現(xiàn)。大部分算法是操作List
的,但一部分對所有類型的集合都是可用的。部分算法有排序、搜索、混編、最大最小值。
39.大寫的O是什么?舉幾個(gè)例子?
大寫的O
描述的是,就數(shù)據(jù)結(jié)構(gòu)中的一系列元素而言,一個(gè)算法的性能。Collection
類就是實(shí)際的數(shù)據(jù)結(jié)構(gòu),我們通常基于時(shí)間、內(nèi)存和性能,使用大寫的O
來選擇集合實(shí)現(xiàn)。比如:例子1:ArrayList的get(index i)
是一個(gè)常量時(shí)間操作,它不依賴list
中元素的數(shù)量。所以它的性能是O(1)
。例子2:一個(gè)對于數(shù)組或列表的線性搜索的性能是O(n)
,因?yàn)槲覀冃枰闅v所有的元素來查找需要的元素。
40.與Java
集合框架相關(guān)的有哪些最好的實(shí)踐?
(1)根據(jù)需要選擇正確的集合類型。比如,如果指定了大小,我們會(huì)選用Array
而非ArrayList
。如果我們想根據(jù)插入順序遍歷一個(gè)Map
,我們需要使用TreeMap
。如果我們不想重復(fù),我們應(yīng)該使用Set
。
(2)一些集合類允許指定初始容量,所以如果我們能夠估計(jì)到存儲(chǔ)元素的數(shù)量,我們可以使用它,就避免了重新哈希或大小調(diào)整。
(3)基于接口編程,而非基于實(shí)現(xiàn)編程,它允許我們后來輕易地改變實(shí)現(xiàn)。
(4)總是使用類型安全的泛型,避免在運(yùn)行時(shí)出現(xiàn)ClassCastException
。
(5)使用JDK提供的不可變類作為Map
的key
,可以避免自己實(shí)現(xiàn)hashCode()
和equals()
。
(6)盡可能使用Collections
工具類,或者獲取只讀、同步或空的集合,而非編寫自己的實(shí)現(xiàn)。它將會(huì)提供代碼重用性,它有著更好的穩(wěn)定性和可維護(hù)性。