★★★★★集合框架:用於存儲數據的容器。
特點:
1:對象封裝數據,對象多了也需要存儲。集合用於存儲對象。
2:對象的個數確定可以使用數組,但是不確定怎麼辦?可以用集合。因爲集合是可變長度的。
集合和數組的區別:
1:數組是固定長度的;集合可變長度的。
2:數組可以存儲基本數據類型,也可以存儲引用數據類型;集合只能存儲引用數據類型。
3:數組存儲的元素必須是同一個數據類型;集合存儲的對象可以是不同數據類型。
數據結構:就是容器中存儲數據的方式。
對於集合容器,有很多種。因爲每一個容器的自身特點不同,其實原理在於每個容器的內部數據結構不同。
集合容器在不斷向上抽取過程中。出現了集合體系。
在使用一個體系時,原則:參閱頂層內容。建立底層對象。
1 Iterator接口
1.1 Iterator
< java.util >-- 迭代器:是一個接口—Iterator接口,其作用:用於取集合中的元素。
在Iterator接口中定義了三個方法:
|
hasNext |
next |
|
|
remove |
每一個集合都有自己的數據結構(就是容器中存儲數據的方式),都有特定的取出自己內部元素的方式。爲了便於操作所有的容器,取出元素。將容器內部的取出方式按照一個統一的規則向外提供,這個規則就是Iterator接口,使得對容器的遍歷操作與其具體的底層實現相隔離,達到解耦的效果。
也就說,只要通過該接口就可以取出Collection集合中的元素,至於每一個具體的容器依據自己的數據結構,如何實現的具體取出細節,這個不用關心,這樣就降低了取出元素和具體集合的耦合性。
Iterator it = coll.iterator();//獲取容器中的迭代器對象,至於這個對象是是什麼不重要。這對象肯定符合一個規則Iterator接口。
public static voidmain(String[] args) {
Collection coll = new ArrayList();
coll.add("abc0");
coll.add("abc1");
coll.add("abc2");
//--------------方式1----------------------
Iterator it = coll.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//---------------方式2用此種----------------------
for(Iterator it =coll.iterator();it.hasNext(); ){
System.out.println(it.next());
}
}
使用Iterator迭代器來進行刪除,則不會出現併發修改異常。
因爲:在執行remove操作時,同樣先執行checkForComodification(),然後會執行ArrayList的remove()方法,該方法會將modCount值加1,這裏我們將expectedModCount=modCount,使之保持統一。
1.2 ListIterator
上面可以看到,Iterator只提供了刪除元素的方法remove,如果我們想要在遍歷的時候添加元素怎麼辦?
ListIterator接口繼承了Iterator接口,它允許程序員按照任一方向遍歷列表,迭代期間修改列表,並獲得迭代器在列表中的當前位置。
使用ListIterator來對list進行邊遍歷邊添加元素操作:
public static void main(String[] args)
{
ArrayList<String> aList = new ArrayList<String>();
aList.add("bbc");
aList.add("abc");
aList.add("ysc");
aList.add("saa");
System.out.println("移除前:" + aList);
ListIterator<String> listIt = aList.listIterator();
while(listIt.hasNext())
{
if("abc".equals(listIt.next()))
{
listIt.add("haha");
}
}
System.out.println("移除後:" + aList);
}
2 Collection接口
--< java.util >--Collection接口:
Collection:
|--List:有序(元素存入集合的順序和取出的順序一致),元素都有索引。元素可以重複。
|--Set:無序(存入和取出順序有可能不一致),不可以存儲重複元素。必須保證元素唯一性。
1.添加:
add(object):添加一個元素
addAll(Collection) :添加一個集合中的所有元素。
2.刪除:
clear():將集合中的元素全刪除,清空集合。
remove(obj) :刪除集合中指定的對象。注意:刪除成功,集合的長度會改變。
removeAll(collection) :刪除部分元素。部分元素和傳入Collection一致。
3.判斷:
boolean contains(obj) :集合中是否包含指定元素 。
boolean containsAll(Collection) :集合中是否包含指定的多個元素。
boolean isEmpty():集合中是否有元素。
4.獲取:
int size():集合中有幾個元素。
5.取交集:
boolean retainAll(Collection) :對當前集合中保留和指定集合中的相同的元素。如果兩個集合元素相同,返回flase;如果retainAll修改了當前集合,返回true。
6.獲取集合中所有元素:
Iterator iterator():迭代器
7.將集合變成數組:
toArray();
2.1 List接口
--< java.util >-- List接口:
List本身是Collection接口的子接口,具備了Collection的所有方法。現在學習List體系特有的共性方法,查閱方法發現List的特有方法都有索引,這是該集合最大的特點。
List:有序(元素存入集合的順序和取出的順序一致)元素都有索引。元素可以重複。(有序可重複)
|--ArrayList:底層的數據結構是數組,線程不同步,ArrayList替代了Vector,查詢元素的速度非常快。默認大小10,1.5倍長度擴容。
|--LinkedList:底層的數據結構是鏈表,線程不同步,增刪元素的速度非常快。
|--Vector:底層的數據結構就是數組,線程同步,Vector無論查詢和增刪都巨慢。默認大 小10,2倍長度擴容。
1.添加:
add(index,element) :在指定的索引位插入元素。
addAll(index,collection) :在指定的索引位插入一堆元素。
2.刪除:
remove(index) :刪除指定索引位的元素。 返回被刪的元素。
3.獲取:
Object get(index) :通過索引獲取指定元素。
int indexOf(obj):獲取指定元素第一次出現的索引位,如果該元素不存在返回-1;
所以,通過-1,可以判斷一個元素是否存在。
int lastIndexOf(Object o) :反向索引指定元素的位置。
List subList(start,end):獲取子列表。
4.修改:
Object set(index,element) :對指定索引位進行元素的修改。
5.獲取所有元素:
ListIterator listIterator():list集合特有的迭代器。
List集合支持對元素的增、刪、改、查。
List集合因爲角標有了自己的獲取元素的方式: 遍歷。
for(int x=0;x<list.size(); x++){
sop("get:"+list.get(x));
}
在進行list列表元素迭代的時候,如果想要在迭代過程中,想要對元素進行操作的時候,比如滿足條件添加新元素。會發生.ConcurrentModificationException併發修改異常。
導致的原因是:
集合引用和迭代器引用在同時操作元素,通過集合獲取到對應的迭代器後,在迭代中,進行集合引用的元素添加,迭代器並不知道,所以會出現異常情況。
如何解決呢?
既然是在迭代中對元素進行操作,找迭代器的方法最爲合適.可是Iterator中只有hasNext,next,remove方法.通過查閱的它的子接口,ListIterator,發現該列表迭代器接口具備了對元素的增、刪、改、查的動作。
ListIterator是List集合特有的迭代器。
ListIterator it =list.listIterator;//取代Iterator it = list.iterator;
方法摘要 |
|
|
|
|
hasNext |
|
hasPrevious |
next |
|
|
nextIndex |
previous |
|
|
previousIndex |
|
remove |
|
可變長度數組的原理:
當元素超出數組長度,會產生一個新數組,將原數組的數據複製到新數組中,再將新的元素添加到新數組中。
ArrayList:是按照原數組的50%延長。構造一個初始容量爲 10 的空列表。
Vector:是按照原數組的100%延長。
注意:對於list集合,底層判斷元素是否相同,其實用的是元素自身的equals方法完成的。所以建議元素都要複寫equals方法,建立元素對象自己的比較相同的條件依據。
LinkedList:的特有方法。
addFirst();
addLast();
在jdk1.6以後。
offerFirst();
offerLast();
getFirst():獲取鏈表中的第一個元素。如果鏈表爲空,拋出NoSuchElementException;
getLast();獲取鏈表中的最後一個元素。如果鏈表爲空,拋出NoSuchElementException;
在jdk1.6以後。
peekFirst();獲取鏈表中的第一個元素。如果鏈表爲空,返回null。
peekLast();
removeFirst():獲取鏈表中的第一個元素,但是會刪除鏈表中的第一個元素。如果鏈表爲空,拋出NoSuchElementException
removeLast();
在jdk1.6以後。
pollFirst();獲取鏈表中的第一個元素,但是會刪除鏈表中的第一個元素。如果鏈表爲空,返回null。
pollLast();
2.2 Set接口
< java.util >-- Set集合無序,add()相同元素則添加失敗,返回flase。:
數據結構:數據的存儲方式;
Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一種,迭代器。
|--HashSet:底層數據結構是哈希表,HashSet是集合,無序,高效,線程不同步。
|--LinkedHashSet:存取順序一致(用鏈表維護),線程不同步,是hashset的子類。
|--TreeSet:元素唯一,有序(按照元素自身執行順序),線程不同步(不按原有數組的順序)。TreeSet底層的數據結構就是二叉樹(平衡二叉排序樹)。
|--EnumSet 只能保存同一類型元素。
HashSet、TreeSet、LinkedHashSet的區別:HashSet只去重,TreeSet去重並排序,LinkedHashSet去重並保留插入順序
===HashSet 哈希表原理===
採用哈希表存儲結構。
1:對對象元素中的關鍵字進行哈希算法運算,得結果爲哈希值(也是這個元素的位置)。
2:存儲哈希值的結構,我們稱爲哈希表,在哈希表中查找對應的哈希值對應位置,
3:如果哈希值出現衝突,再次判斷這個關鍵字對應的對象是否相同:
如果對象相同,就不存儲,因爲元素重複;
如果對象不同,就存儲,在原來對象的哈希值基礎 +1順延。
4:既然哈希表根據哈希值存儲,爲提高效率,最好保證對象關鍵字的唯一性。 可儘量少的判斷關鍵字對應的對象是否相同,提高了哈希表的操作效率。
高效:保證關鍵字唯一性,即爲上述第三步所述,也可以用以下敘述:
HashSet集合保證元素唯一性:通過元素的hashCode()和equals()完成的。
當元素的hashCode值相同時,才繼續判斷元素的equals是否爲true。
如果爲true,那麼視爲相同元素,不存。如果爲false,那麼存儲。
如果hashCode值不同,那麼不判斷equals,從而提高對象比較的速度。
對於ArrayList集合,判斷元素是否存在,或者刪元素底層依據都是equals方法。
對於HashSet集合,判斷元素是否存在,或者刪除元素,底層依據的是hashCode方法和equals方法。
===TreeSet原理===
採用二叉樹(二叉平衡排序樹)存儲結構 (或紅黑樹)
TreeSet用於對Set集合進行元素的指定順序排序,要依據元素自身的比較性( 如果元素不具備比較性,在運行時會發生ClassCastException異常)所以需要元素實現Comparable接口,複寫compareTo方法(根據指定需求),強制讓對象元素具備比較性,否則比較時引發ClassCastException異常。
TreeSet支持兩種排序方法:自然排序和定製排序;默認採用自然排序。
原理:當把一個對象添(必須實現Comparable接口)加進TreeSet時,TreeSet調用該對象的compareTo(Objectobj)方法與容器中的其他對象比較大小,然後根據紅黑樹算法決定它的存儲位置。 如果兩個對象通過compareTo(Object obj)比較相等,return0,視爲兩對象重複,不存儲。(通過此方法保證了對象的唯一性)
注意:在進行比較時,如果判斷元素不唯一,比如,同姓名,同年齡,才視爲同一個人。
在判斷時,需要分主要、次要條件,當主要條件相同時,再判斷次要條件,按照次要條件排序。
TreeSet集合排序有兩種方式,Comparable和Comparator區別:
1:讓元素自身具備比較性,需要元素對象實現Comparable接口,覆蓋compareTo方法。
2:讓集合自身具備比較性,需要定義一個實現了Comparator接口的比較器,並覆蓋compare方法,並將該類對象作爲實際參數傳遞給TreeSet集合的構造函數。第二種方式較爲靈活。
3 Map接口
Map
|--Hashtable:底層是哈希散列表數據結構,線程同步。不可以存儲null鍵,null值。不可序列化,使用bucket結構體表示單個元素,使用雙重散列法(閉散列法)解決衝突(二度哈希,size>length時要進行模運算)。
|--HashMap:底層是哈希表數據結構(鏈地址法解決衝突),線程不同步。可存一個null鍵和多個null值。替代了Hashtable. 但可通過Map m = Collections.synchronizeMap(hashMap)實現同步;
|--LinkedHashMap,採用雙向鏈表數據結構連接起來所有的entry,保證了存入和取出順序一致,即鏈表有序;線程不同步。
|--TreeMap:底層是二叉樹結構(平衡二叉排序樹),可以對map集合中的鍵進行指定順序的排序。
Map集合存儲和Collection有着很大不同:
Collection一次存一個元素,是單列集合;
Map一次存一對元素,是雙列集合。Map存儲的一對元素:鍵--值,鍵(key)與值(value)間有對應(映射)關係。
特點:要保證Map中鍵的唯一性。
1:添加。
put(key,value):當存儲的鍵相同時,新的值會替換老的值,並將老值返回。如果鍵沒有重複,返回null。
void putAll(Map);
2:刪除。
void clear():清空
value remove(key) :刪除指定鍵。
3:判斷。
boolean isEmpty():
boolean containsKey(key):是否包含key
boolean containsValue(value):是否包含value
4:取出。
int size():返回長度
value get(key) :通過指定鍵獲取對應的值。如果返回null,可以判斷該鍵不存在。當然有特殊情況,就是在hashmap集合中,是可以存儲null鍵null值的。
Collection values():獲取map集合中的所有的值。
5:想要獲取map中的所有元素
原理:map沒有迭代器,collection具備迭代器,只要將map轉成Set集合,就可使用迭代器。之所以轉成set,是因爲map集合具備鍵的唯一性,其實set集合就來自於map,set集合底層其實用的就是map的方法。
★ 把map集合轉成set的方法:(決定了兩種遍歷方式)
Set keySet();
Set entrySet();//取的是鍵和值的映射關係。
Entry就是Map接口中的內部接口;
爲什麼要定義在map內部呢?entry是訪問鍵值關係的入口,是map的入口,訪問的是map中的鍵值對。
---------------------------------------------------------
取出map集合中所有元素的方式一:keySet()方法。
可以將map集合中的鍵都取出存放到set集合中。對set集合進行迭代。迭代完成,再通過get方法對獲取到的鍵進行值的獲取。
Set keySet = map.keySet();
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next();
Objectvalue = map.get(key);
System.out.println(key+":"+value); }
取出map集合中所有元素的方式二:entrySet()方法。
Set entrySet = map.entrySet();
Iterator it =entrySet.iterator();
while(it.hasNext()) {
Map.Entry me =(Map.Entry)it.next();
System.out.println(me.getKey()+"::::"+me.getValue());
}
使用集合的技巧:
看到Array就是數組結構,有角標,查詢速度很快。
看到link就是鏈表結構:增刪速度快,而且有特有方法。addFirst; addLast;removeFirst(); removeLast();getFirst();getLast();
看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到該結構中的元素必須覆蓋hashCode和equals方法。
看到tree就是二叉樹,就要想到排序,就想要用到比較。
比較的兩種方式:
一個是Comparable:覆蓋compareTo方法;
一個是Comparator:覆蓋compare方法。
LinkedHashSet,LinkedHashMap:這兩個集合可以保證哈希表有存入順序和取出順序一致,保證哈希表有序。
集合使用場景?
當存儲一個元素時,用Collection。當存儲對象之間存在着映射關係時,用Map集合。
保證唯一,就用Set。不保證唯一,就用List。
4 綜合總結
4.1 集合工具Collections
Collections:集合工具類,它的出現給集合操作提供了更多的功能。這個類不需要創建對象,內部提供的都是靜態方法。
靜態方法:
Collections.sort(list);//list集合進行元素的自然順序排序。
Collections.sort(list,new ComparatorByLen());//按指定的比較器方法排序。
class ComparatorByLen implements Comparator<String>{
public int compare(String s1,String s2){
int temp = s1.length()-s2.length();
return temp==0?s1.compareTo(s2):temp;
}
}
Collections.max(list);//返回list中字典順序最大的元素。
int index = Collections.binarySearch(list,"zz");//二分查找,返回角標。
Collections.reverseOrder();//逆向反轉排序。
Collections.shuffle(list);//隨機對list中的元素進行位置的置換。
將非同步集合轉成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
原理:定義一個類,將集合所有的方法加同一把鎖後返回。
Collection 和 Collections的區別:
Collections是個java.util下的類,是針對集合類的一個工具類,提供一系列靜態方法,實現對集合的查找、排序、替換、線程安全化(將非同步的集合轉換成同步的)等操作。
Collection是個java.util下的接口,它是各種集合結構的父接口,繼承於它的接口主要有Set和List,提供了關於集合的一些操作,如插入、刪除、判斷一個元素是否其成員、遍歷等。
4.2 數組 Arrays
用於操作數組對象的工具類,裏面都是靜態方法。
數組=》集合:asList方法,將數組轉換成list集合。
String[] arr ={"abc","kk","qq"};
List<String> list =Arrays.asList(arr);//將arr數組轉成list集合。
將數組轉換成集合,有什麼好處呢?用aslist方法,將數組變成集合;
可以通過list集合中的方法來操作數組中的元素:isEmpty()、contains、indexOf、set;
注意(侷限性):數組是固定長度,不可以使用集合對象增加或者刪除等,會改變數組長度的功能方法。比如add、remove、clear。(會報不支持操作異常UnsupportedOperationException);
如果數組中存儲的引用數據類型,直接作爲集合的元素可以直接用集合方法操作。
如果數組中存儲的是基本數據類型,asList會將數組實體作爲集合元素存在。
集合=》數組:用的是Collection接口中的toArray()方法;
如果給toArray傳遞的指定類型的數據長度小於了集合的size,那麼toArray方法,會自定再創建一個該類型的數據,長度爲集合的size。
如果傳遞的指定的類型的數組的長度大於了集合的size,那麼toArray方法,就不會創建新數組,直接使用該數組即可,並將集合中的元素存儲到數組中,其他爲存儲元素的位置默認值null。
所以,在傳遞指定類型數組時,最好的方式就是指定的長度和size相等的數組。
將集合變成數組後有什麼好處?限定了對集合中的元素進行增刪操作,只要獲取這些元素即可。
4.3 LinkedHashSet和LinkedHashMap比較
兩者實現相同,只是前者對後者做了一層包裝,即LinkedHashSet裏面有一個LinkedHashMap(適配器模式)。下面說其實現。
LinkedHashMap,可存null鍵null值,從名字上可以看出是linkedlist和HashMap的混合體,同時滿足HashMap和linked list的某些特性。可將LinkedHashMap看作採用linked list增強的HashMap。
事實上LinkedHashMap是HashMap的直接子類,LinkedHashMap在HashMap的基礎上採用雙向鏈表(doubly-linked list)的形式將所有entry連接起來,保證元素的迭代順序跟插入順序相同。
除了迭代順序不變,還有一個好處:迭代時不需要遍歷整個table,只需要直接遍歷header指針指向的雙向鏈表即可,(LinkedHashMap的迭代時間就只跟entry的個數相關,而跟table的大小無關。)
有兩個參數可以影響LinkedHashMap的性能:初始容量(initalcapacity)和負載係數(load factor)。初始容量指定了初始table的大小,負載係數用來指定自動擴容的臨界值。當entry的數量超過capacity*load_factor時,容器將自動擴容並重新哈希。對於插入元素較多的場景,將初始容量設大可以減少重新哈希的次數。
向LinkedHashMap或LinkedHashSet添加對象時,需要關心兩個方法:hashCode()方法決定了對象會被放到哪個bucket裏,當多個對象的哈希值衝突時,equals()方法決定了這些對象是否是“同一個對象”。此時需要將自定義的對象 *@Override*hashCode()和equals()方法。
5 Java集合常見題目
1.Java集合類框架的基本接口有哪些?
Java集合類提供了一套設計良好的支持對一組對象進行操作的接口和類。Java集合類裏面最基本的接口有:
Collection:代表一組對象,每一個對象都是它的子元素。
Set:不包含重複元素的Collection。
List:有順序的collection,並且可以包含重複元素。
Map:可以把鍵(key)映射到值(value)的對象,鍵不能重複。
2.爲什麼集合類沒有實現Cloneable和Serializable接口?
集合類接口指定了一組叫做元素的對象。集合類接口的每一種具體的實現類都可以選擇以它自己的方式對元素進行保存和排序。有的集合類允許重複的鍵,有些不允許。
克隆(cloning)或者是序列化(serialization)的語義和含義是跟具體的實現相關的。因此,應該由集合類的具體實現來決定如何被克隆或者是序列化。
3.什麼是迭代器(Iterator)?
Iterator接口提供了很多對集合元素進行迭代的方法。每一個集合類都包含了可以返回迭代器實例的迭代方法。迭代器可以在迭代的過程中刪除底層集合的元素,安全。
4.Iterator和ListIterator的區別是什麼?
=》Iterator可用來遍歷Set和List集合,但是ListIterator只能用來遍歷List。
=》Iterator對集合只能是前向遍歷,ListIterator既可以前向也可以後向。
=》ListIterator實現了Iterator接口,幷包含其他的功能,比如:增加元素,替換元素,獲取前一個和後一個元素的索引,等等。
5.快速失敗(fail-fast)和安全失敗(fail-safe)的區別是什麼?
Iterator的安全失敗是基於對底層集合做拷貝,因此,它不受源集合上修改的影響。java.util包下面的所有的集合類都是快速失敗的,而java.util.concurrent包下面的所有的類都是安全失敗的。快速失敗的迭代器會拋出ConcurrentModificationException異常,而安全失敗的迭代器永遠不會拋出這樣的異常。
6.Java中的HashMap的工作原理是什麼?
Java中的HashMap是以鍵值對(key-value)的形式存儲元素的。HashMap需要一個hash函數,它使用hashCode()和equals()方法來向集合/從集合添加和檢索元素。
當調用put()方法的時候,HashMap會計算key的hash值,然後把鍵值對存儲在集合中合適的索引上。如果key已經存在了,value會被更新成新值。HashMap的一些重要的特性是它的容量(capacity),負載因子(load factor)和擴容極限(threshold resizing)。
擴容牽扯到rehash的過程:增加1倍,然後重新計算hash值並且搬運元素到新的哈希表當中。
get()方法,同樣是……
7. hashCode()和equals()方法的重要性體現在什麼地方?
Java中的HashMap使用hashCode()和equals()方法來確定鍵值對的索引,當根據鍵獲取值的時候也會用到這兩個方法。如果沒有正確的實現這兩個方法,兩個不同的鍵可能會有相同的hash值,因此,可能會被集合認爲是相等的。而且,這兩個方法也用來發現重複元素。所以這兩個方法的實現對HashMap的精確性和正確性是至關重要的。
8.HashMap和Hashtable有什麼區別?
HashMap和Hashtable都實現了Map接口,很多特性相似。但有不同點:
HashMap允許鍵和值是null,而Hashtable不允許鍵或者值是null。
Hashtable是同步的,而HashMap不是。因此,HashMap更適合於單線程環境,而Hashtable適合於多線程環境。
HashMap提供了可供應用迭代的鍵的集合keySet(),因此,HashMap是快速失敗fast-fail的。
另一方面,Hashtable提供了對鍵的列舉(Enumeration)。一般認爲Hashtable是一個遺留的類。
9.數組(Array)和列表(ArrayList)有什麼區別?什麼時候應該使用Array而不是ArrayList?
不同點:
定義上:Array可以包含基本類型和對象類型,ArrayList只能包含對象類型。
容量上:Array大小固定,ArrayList的大小是動態變化的。
操作上:ArrayList提供更多的方法和特性,如:addAll(),removeAll(),iterator()等等。
使用基本數據類型或者知道數據元素數量的時候可以考慮Array;
ArrayList處理固定數量的基本類型數據類型時會自動裝箱來減少編碼工作量,但是相對較慢。
10.ArrayList和LinkedList有什麼區別?
兩者都實現了List接口,他們有以下不同點:
數據結構上:
ArrayList是基於索引的數組形式,可隨機訪問元素, 時間複雜度O(1);
LinkedList是元素列表的形式存儲它的數據,每一個元素都和它的前一個和後一個元素鏈接在一起,在這種情況下,查找某個元素的時間複雜度是O(n)。
操作上:
ArrayList添加,刪除操作比較慢,重新計算大小或者是更新索引。
LinkedList的插入,添加,刪除操作速度更快,不需要更新索引。
內存上:
LinkedList比ArrayList更佔內存,因爲LinkedList爲每一個節點存儲了兩個引用,一個指向前一個元素,一個指向下一個元素。
11.Comparable和Comparator接口是幹什麼的?列出它們的區別。
Java提供了只包含一個compareTo()方法的Comparable接口。這個方法可以個給兩個對象排序。具體來說,它返回負數,0,正數來表明輸入對象小於,等於,大於已經存在的對象。
Java提供了包含compare()和equals()兩個方法的Comparator接口。compare()方法用來給兩個輸入參數排序,返回負數,0,正數表明第一個參數是小於,等於,大於第二個參數。equals()方法需要一個對象作爲參數,它用來決定輸入參數是否和comparator相等。只有當輸入參數也是一個comparator並且輸入參數和當前comparator的排序結果是相同的時候,這個方法才返回true。
12.什麼是Java優先級隊列(Priority Queue)?
PriorityQueue是一個基於優先級堆的無界有序隊列,它的元素是按照自然順序(natural order)排序的。在創建的時候,我們可以給它提供一個負責給元素排序的比較器。PriorityQueue不允許null值,因爲他們沒有自然順序,或者說他們沒有任何的相關聯的比較器。最後,PriorityQueue不是線程安全的,入隊和出隊的時間複雜度是O(log(n))。
13.你瞭解大O符號(big-O notation)麼?你能給出不同數據結構的例子麼?
大O:描述了當數據結構裏面的元素增加的時候,算法的規模或者是性能在最壞的場景下有多麼好。
大O符號也可用來描述其他的行爲,比如:內存消耗。因爲集合類實際上是數據結構,我們一般使用大O符號基於時間,內存和性能來選擇最好的實現。大O符號可以對大量數據的性能給出一個很好的說明。
14.如何權衡是使用無序的數組還是有序的數組?
有序數組最大的好處在於查找的時間複雜度是O(log n),而無序數組是O(n)。有序數組的缺點是插入操作的時間複雜度是O(n),因爲值大的元素需要往後移動來給新元素騰位置。相反,無序數組的插入時間複雜度是常量O(1)。
所以,查找操作多的時候,使用有序;增刪操作多的使用無序的即可。
15.Java集合類框架的最佳實踐有哪些?
根據應用的需要正確選擇要使用的集合的類型對性能非常重要,比如:假如元素的大小是固定的,而且能事先知道,我們就應該用Array而不是ArrayList。
有些集合類允許指定初始容量。因此,如果我們能估計出存儲的元素的數目,我們可以設置初始容量來避免重新計算hash值或者是擴容。
爲了類型安全,可讀性和健壯性的原因總是要使用泛型。同時,使用泛型還可以避免運行時的ClassCastException。
使用JDK提供的不變類(immutable class)作爲Map的鍵可以避免爲我們自己的類實現hashCode()和equals()方法。
編程的時候接口優於實現。
底層的集合實際上是空的情況下,返回長度是0的集合或者是數組,不要返回null。
16.Enumeration接口和Iterator接口的區別有哪些?
Enumeration速度是Iterator的2倍,同時佔用更少的內存。
但是,Iterator遠遠比Enumeration安全,因爲其他線程不能夠修改正在被iterator遍歷的集合裏面的對象。同時,Iterator允許調用者刪除底層集合裏面的元素,這對Enumeration來說是不可能的。
17.HashSet和TreeSet有什麼區別?
HashSet是由一個哈希表來實現的,元素無,add(),remove(),contains()方法的時間複雜度是O(1)。
另一方面,TreeSet是由一個樹形結構(平衡二叉排序樹)來實現的,它裏面的元素是有序的。因此,add(),remove(),contains()方法的時間複雜度是O(logn)。
5.1集合框架基礎
1.Java集合框架是什麼?說出一些集合框架的優點?
每種編程語言中都有集合,最初的Java版本包含幾種集合類:Vector、Stack、HashTable和Array。隨着集合的廣泛使用,Java1.2提出了囊括所有集合接口、實現和算法的集合框架。在保證線程安全的情況下使用泛型和併發集合類,Java已經經歷了很久。它還包括在Java併發包中,阻塞接口以及它們的實現。集合框架的部分優點如下:
(1)使用核心集合類降低開發成本,而非實現我們自己的集合類。
(2)隨着使用經過嚴格測試的集合框架類,代碼質量會得到提高。
(3)通過使用JDK附帶的集合類,可以降低代碼維護成本。
(4)複用性和可操作性。
2.集合框架中的泛型有什麼優點?
Java1.5引入了泛型,所有的集合接口和實現都大量地使用它。
泛型允許我們爲集合提供一個可以容納的對象類型,因此,如果你添加其它類型的任何元素,它會在編譯時報錯。這避免了在運行時出現ClassCastException,因爲你將會在編譯時得到報錯信息。泛型也使得代碼整潔,我們不需要使用顯式轉換和instanceOf操作符。
它也給運行時帶來好處,因爲不會產生類型檢查的字節碼指令。
3.Java集合框架的基礎接口有哪些?
Collection爲集合層級的根接口。一個集合代表一組對象,這些對象即爲它的元素。Java平臺不提供這個接口任何直接的實現。
Set是一個不能包含重複元素的集合。這個接口對數學集合抽象進行建模,被用來代表集合,就如一副牌。
List是一個有序集合,可以包含重複元素。你可以通過它的索引來訪問任何元素。List更像長度動態變換的數組。
Map是一個將key映射到value的對象.一個Map不能包含重複的key:每個key最多隻能映射一個value。
一些其它的接口有Queue、Dequeue、SortedSet、SortedMap和ListIterator。
4.爲何Collection不從Cloneable和Serializable接口繼承?
Collection接口指定一組對象,對象即爲它的元素。如何維護這些元素由Collection的具體實現決定。例如,一些如List的Collection實現允許重複的元素,而其它的如Set就不允許。很多Collection實現有一個公有的clone方法。然而,把它放到集合的所有實現中也是沒有意義的。這是
因爲Collection是一個抽象表現,而重要的是實現。
當與具體實現打交道的時候,克隆或序列化的語義和含義才發揮作用。所以,具體實現應該決定如何對它進行克隆或序列化,或它是否可以被克隆或序列化。
在所有的實現中授權克隆和序列化,最終導致更少的靈活性和更多的限制。特定的實現應該決定它是否可以被克隆和序列化。
5.爲何Map接口不繼承Collection接口?
儘管Map接口和它的實現也是集合框架的一部分,但Map不是集合,集合也不是Map。因此,Map繼承Collection無論誰繼承誰都毫無意義。
如果Map繼承Collection接口,那麼元素去哪兒?Map包含key-value對,它提供抽取key或value列表集合的方法,但是它不適合“一組對象”規範。
5.2 Iterator
6.Iterator是什麼?
Iterator接口提供遍歷任何Collection的接口。我們可以從一個Collection中使用迭代器方法來獲取迭代器實例。迭代器取代了Java集合框架中的Enumeration。迭代器允許調用者在迭代過程中移除元素。
7.Enumeration和Iterator接口的區別?
Enumeration的速度是Iterator的兩倍,使用更少的內存。Enumeration是非常基礎的,也滿足了基礎的需要。但是,Iterator更加安全,因爲當一個集合正在被遍歷的時候,它會阻止其它線程去修改集合。
迭代器取代了Java集合框架中的Enumeration,並允許調用者從集合中移除元素,而Enumeration不能做到。爲了使它的功能更加清晰,迭代器方法名已經經過改善。
8.爲何沒有像Iterator.add()這樣的方法,向集合中添加元素?
語義不明,已知的是,Iterator的協議不能確保迭代的次序。然而要注意,ListIterator沒有提供一個add操作,它要確保迭代的順序。
9.爲何迭代器沒有一個方法可以直接獲取下一個元素,而不需要移動遊標?
它可以在當前Iterator的頂層實現,但是它用得很少,如果將它加到接口中,每個繼承都要去實現它,這沒有意義。
10.Iterater和ListIterator之間有什麼區別?
(1)我們可以使用Iterator來遍歷Set和List集合,而ListIterator只能遍歷List。
(2)Iterator只可以向前遍歷,而ListIterator可以雙向遍歷。
(3)ListIterator從Iterator接口繼承,然後添加了一些額外的功能,比如添加一個元素、替換一個元素、獲取前面或後面元素的索引位置。
11.遍歷一個List有哪些不同的方式?
使用迭代器更加線程安全,因爲它可以確保,在當前遍歷的集合元素被更改的時候,它會拋出ConcurrentModificationException。
List<String> strList = new ArrayList<>();
//使用for-each循環
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);
}
12.通過迭代器fail-fast屬性,你明白了什麼?
每次嘗試獲取下一個元素時,Iterator fail-fast屬性檢查當前集合結構裏的任何改動。如有改動,則拋出異常ConcurrentModificationException。Collection中所有Iterator的實現都是按fail-fast來設計的(ConcurrentHashMap和CopyOnWriteArrayList這類併發集合類除外)。
13.fail-fast與fail-safe有什麼區別?
(1)Java.util包中的所有集合類都被設計爲fail-fast的,而java.util.concurrent中的集合類都爲fail-safe的。
(2)fail-fast檢測集合結構改變的原理,Iterator直接訪問集合的數據結構,它保留一個標誌”mods”,在Iterator每次調用hasNext()或者是next()方法時,首先檢測”mods”狀態,如果結構已經改變,則拋出異常。
fail-safe Iterator的實現原理是,先將原集合拷貝一份,在拷貝上開展遍歷,因此不會引起ConcurrentModification異常。因此,Fail Safe Iterator存在兩個缺陷: 額外的空間開銷 和遍歷數據不一定是最新的。
14.在迭代一個集合的時候,如何避免ConcurrentModificationException?
在遍歷一個集合的時候,我們可以使用併發集合類來避免ConcurrentModificationException,比如使用CopyOnWriteArrayList,而不是ArrayList。
即使用java.uitl.concurrenet中的集合類代替java.util包下的集合類。
15.爲何Iterator接口沒有具體的實現?
Iterator接口定義了遍歷集合的方法,但它的實現則是集合實現類的責任。每個能夠返回用於遍歷的Iterator的集合類都有它自己的Iterator實現內部類。
這就允許集合類去選擇迭代器是fail-fast還是fail-safe的。比如,ArrayList迭代器是fail-fast的,而CopyOnWriteArrayList迭代器是fail-safe的。
16.UnsupportedOperationException是什麼?
UnsupportedOperationException是用於表明操作不支持的異常。在JDK類中已被大量運用,在集合框架java.util.Collections.UnmodifiableCollection將會在所有add和remove操作中拋出這個異常。
5.3 Map/List/Set/Queue/Stack
17.在Java中,HashMap是如何工作的?
HashMap在Map.Entry靜態內部類實現中存儲key-value鍵值對。使用“數組和鏈表”的存儲結構,總體使用“鏈地址法”來解決哈希衝突。
HashMap使用哈希算法,在put和get方法中,它都使用了hashCode()和equals()方法。
put()方法:首先,HashMap使用Key hashCode()和哈希算法來找出存儲key-value對的索引。Entry存儲在LinkedList中,所以如果存在entry,它使用equals()方法來檢查傳遞的key是否已經存在,如果存在,它會覆蓋value,如果不存在,它會創建一個新的entry然後保存。
get()方法:當我們通過傳遞key調用get方法時,它再次使用hashCode()來找到數組中的索引,然後使用equals()方法找出正確的Entry,然後返回它的值。
其它關於HashMap比較重要的問題是容量、負荷係數和閥值調整。HashMap默認的初始容量是32,負荷係數是0.75。閥值是爲負荷係數乘以容量,無論何時我們嘗試添加一個entry,如果map的大小比閥值大的時候,HashMap會對map的內容進行重新哈希Rehash,且使用更大的容量。容量總是2的冪,所以如果你知道你需要存儲大量的key-value對,比如緩存從數據庫裏面拉取的數據,使用正確的容量和負荷係數對HashMap進行初始化是個不錯的做法。
Rehash算法:如果哈希地址不夠,要對hash表進行擴容,擴容爲原來的2倍,然後將原來hash表中的所有計算好hash地址的元素重新計算hashCode,並且搬到擴容後的hash表後的LinkedList鏈表中。
18.hashCode()和equals()方法有何重要性?
HashMap使用Key對象的hashCode()和equals()方法去決定key-value對的索引。當我們試着從HashMap中獲取值的時候,這些方法也會被用到。如果這些方法沒有被正確地實現,在這種情況下,兩個不同Key也許會產生相同的hashCode()和equals()輸出,HashMap將會認爲它們是相同的,然後覆蓋它們,而非把它們存儲到不同的地方。同樣的,所有不允許存儲重複數據的集合類都使用hashCode()和equals()去查找重複,所以正確實現它們非常重要。equals()和hashCode()的實現應該遵循以下規則:
(1)如果o1.equals(o2),那麼o1.hashCode() == o2.hashCode()總是爲true的。
(2)如果o1.hashCode() == o2.hashCode(),並不意味着o1.equals(o2)會爲true。
19.我們能否使用任何類作爲Map的key?
我們可以使用任何類作爲Map的key,然而在使用它們之前,需要考慮以下幾點:
(1)如果類重寫了equals()方法,它也應該重寫hashCode()方法。
(2)類的所有實例需要遵循與equals()和hashCode()相關的規則。(請參考之前提到的這些規則)
(3)如果一個類沒有使用equals(),你不應該在hashCode()中使用它。
(4)用戶自定義key類的最佳實踐是使之爲不可變的,這樣,hashCode()值可以被緩存起來,擁有更好的性能。不可變的類也可以確保hashCode()和equals()在未來不會改變,這樣就會解決與可變相關的問題了。
比如,我有一個類MyKey,在HashMap中使用它。
那就是爲何String和Integer這些不可變類被作爲HashMap的key大量使用(原因就是防止可變類的修改導致再次利用key查找索引的時候不可復現原來的索引,即查找索引失敗)。
//傳遞給MyKey的name參數被用於equals()和hashCode()中
MyKey key = new MyKey('Pankaj'); //assume hashCode=1234
myHashMap.put(key, 'Value');
// 以下的代碼會改變key的hashCode()和equals()值
key.setName('Amit'); //assume new hashCode=7890
//下面會返回null,因爲HashMap會嘗試查找存儲同樣索引的key,而key已被改變了,匹配失敗,返回null
myHashMap.get(new MyKey('Pankaj'));
20.Map接口提供了哪些不同的集合視圖?
Map接口提供三個集合視圖:
(1)Set keyset():返回map中包含的所有key的一個Set視圖。集合是受map支持的,map的變化會在集合中反映出來,反之亦然。當一個迭代器正在遍歷一個集合時,若map被修改了(除迭代器自身的移除操作以外),迭代器的結果會變爲未定義。集合支持通過Iterator的Remove、Set.remove、removeAll、retainAll和clear操作進行元素移除,從map中移除對應的映射。它不支持add和addAll操作。
(2)Collectionvalues():返回一個map中包含的所有value的一個Collection視圖。這個collection受map支持的,map的變化會在collection中反映出來,反之亦然。當一個迭代器正在遍歷一個collection時,若map被修改了(除迭代器自身的移除操作以外),迭代器的結果會變爲未定義。集合支持通過Iterator的Remove、Set.remove、removeAll、retainAll和clear操作進行元素移除,從map中移除對應的映射。它不支持add和addAll操作。
(3)Set<Map.Entry<K,V>>entrySet():返回一個map鍾包含的所有映射的一個集合視圖。這個集合受map支持的,map的變化會在collection中反映出來,反之亦然。當一個迭代器正在遍歷一個集合時,若map被修改了(除迭代器自身的移除操作,以及對迭代器返回的entry進行setValue外),迭代器的結果會變爲未定義。集合支持通過Iterator的Remove、Set.remove、removeAll、retainAll和clear操作進行元素移除,從map中移除對應的映射。它不支持add和addAll操作。
21.HashMap和HashTable有何不同?
(1)HashMap允許key和value爲null,而HashTable不允許。
(2)HashTable是同步的、線程安全的,而HashMap是線程不安全的;所以HashMap適合單線程環境,HashTable適合多線程環境。
(3)在Java1.4中引入了LinkedHashMap,HashMap的一個子類,假如你想要遍歷順序,你很容易從HashMap轉向LinkedHashMap,但是HashTable不是這樣的,它的順序是不可預知的。
(4)HashMap提供對key的Set進行遍歷,因此它是fail-fast的,但HashTable提供對key的Enumeration進行遍歷,它不支持fail-fast。
(5)HashTable被認爲是個遺留的類,如果你尋求在迭代的時候修改Map,你應該使用CocurrentHashMap。
22.如何決定選用HashMap還是TreeMap?
在Map中,插入、刪除和定位元素等操作,選擇HashMap;如要有序遍歷key集合,選擇TreeMap。
基於你的collection的大小,也許向HashMap中添加元素會更快,將map換爲TreeMap進行有序key的遍歷。
23.ArrayList和Vector有何異同點?
相同點:
(1)兩者都是基於索引的,都是基於數組的。
(2)兩者都維護插入順序,我們可以根據插入順序來獲取元素。
(3)ArrayList和Vector的迭代器實現都是fail-fast的。
(4)ArrayList和Vector兩者允許null值,也可以使用索引值對元素進行隨機訪問。
不同點:
(1)Vector是同步,線程安全,而ArrayList非同步,線程不安全。對於ArrayList,如果迭代時改變列表,應該使用CopyOnWriteArrayList。
(2)但是,ArrayList比Vector要快,它因爲有同步,不會過載。
(3)在使用上,ArrayList更加通用,因爲Collections工具類容易獲取同步列表和只讀列表。
24.Array和ArrayList有何區別?什麼時候更適合用Array?
Array不如ArrayList的地方:
Array容納基本類型和對象,而ArrayList只能容納對象。
Array是大小指定後被固定了,而ArrayList大小是固定的。
Array沒有提供ArrayList那麼多功能,比如addAll、removeAll和iterator等。
但有時候Array比較好用:
(1)如果列表的大小已經指定,大部分情況下是存儲和遍歷它們。
(2)對於遍歷基本數據類型,儘管Collections使用自動裝箱來減輕編碼任務,在指定大小的基本類型的列表上工作也會變得很慢。
(3)如果你要使用多維數組,使用[][]比List<List<>>更容易。
25.ArrayList和LinkedList有何區別?
兩者都實現了List接口,但有不同之處:
(1)ArrayList是一個基於Array和索引的數據結構的實現,在遍歷上:可隨機訪問元素,複雜度爲O(1);
LinkedList是一個基於鏈表的數據結構的實現,存儲的節點數據都只與前一個和下一個節點相連接。在遍歷上:儘管可以利用索引獲取元素,但是內部實現依舊是從起始點開始遍歷,遍歷到索引的節點然後返回元素,時間複雜度爲O(n),速度上比ArrayList要慢。
(2)與ArrayList相比,在LinkedList中插入、添加和刪除一個元素會更快,因爲在一個元素被插入到中間的時候,不會涉及改變數組的大小和更新索引(數據元素的移動)。
(3)LinkedList比ArrayList消耗更多的內存,因爲LinkedList中的每個節點存儲了前後節點的引用;並且LinkedList空間利用率也低於ArrayList,這是基於他們的數據結構的。
26.哪些集合類提供對元素的隨機訪問?
ArrayList、HashMap、TreeMap和HashTable類提供對元素的隨機訪問。
27.EnumSet是什麼?
java.util.EnumSet是使用枚舉類型的集合實現。當集合創建時,枚舉集合中的所有元素必須來自單個指定的枚舉類型,可以是顯示的或隱示的。EnumSet是不同步的,不允許值爲null的元素。它也提供了一些有用的方法,比如copyOf(Collection c)、of(E first,E…rest)和complementOf(EnumSet s)。
28.哪些集合類是線程安全的?
Vector、HashTable、Properties和Stack是同步類,線程安全的,可以在多線程環境下使用。Java1.5併發API包括一些集合類,允許迭代時修改,因爲它們都工作在集合的克隆上,所以它們在多線程環境中是安全的。
29.併發集合類是什麼?
Java1.5併發包(java.util.concurrent)包含線程安全集合類,允許在迭代時修改集合。迭代器被設計爲fail-fast的,會拋出ConcurrentModificationException。一部分類爲:CopyOnWriteArrayList、 ConcurrentHashMap、CopyOnWriteArraySet。
30.BlockingQueue是什麼?
Java.util.concurrent.BlockingQueue是一個隊列,該接口是Java集合框架的一部分,主要用於實現生產者-消費者模式:檢索或移除元素時,會等待隊列變爲非空;添加元素時,會等待隊列中可用空間。
我們不需要擔心等待生產者有可用的空間,或消費者有可用的對象,因爲它都在BlockingQueue的實現類中被處理了。
Java提供了集中BlockingQueue的實現,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。
31.隊列和棧是什麼,列出它們的區別?
棧和隊列兩者都被用來預存儲數據。
隊列:java.util.Queue是一個接口,它的實現類在Java併發包中,允許先進先出(FIFO)檢索元素,但並非總是這樣。Deque接口允許從兩端檢索元素。
棧Stack:是一個擴展自Vector的類,但它允許對元素進行後進先出(LIFO)進行檢索。
而Queue是一個接口。
32.Collections類是什麼?
Java.util.Collections是一個工具類僅包含靜態方法,它們操作或返回集合。它包含操作集合的多態算法,返回一個由指定集合支持的新集合和其它一些內容。
這個類包含集合框架算法的方法,比如折半搜索、排序、混編和逆序等。
5.4排序
33.Comparable和Comparator接口是什麼?
Comparable接口:使用Array或Collection的排序方法時,自定義類需要實現Java提供Comparable接口的compareTo(TOBJ)方法,它被排序方法所使用,應該重寫這個方法,如果“this”對象比傳遞的對象參數更小、相等或更大時,它返回一個負整數、0或正整數。
使用Comparator接口的情景:在大多數實際情況下,我們想根據不同參數進行排序。比如,作爲一個CEO,我想對僱員基於薪資進行排序,一個HR想基於年齡對他們進行排序。這就是我們需要使用Comparator接口的情景。因爲Comparable.compareTo(Object o)方法實現只能基於一個字段進行排序,不能根據需要選擇對象字段來對對象進行排序。
Comparator接口:可以實現兩個對象的特定字段的比較(比如,比較員工這個對象的年齡),該接口的compare(Objecto1, Object o2)方法的實現需要傳遞兩個對象參數,若第一個參數小於、等於、大於第二個參數,返回負整數、0、正整數。
34.Comparable和Comparator接口有何區別?
Comparable和Comparator接口被用來對對象集合或者數組進行排序。
Comparable接口被用來提供對象的自然排序,可使用它來提供基於單個邏輯的排序。
Comparator接口被用來提供不同的排序算法,可根據制定字段選擇需要使用的Comparator來對指定的對象集合進行排序。
35.我們如何對一組對象進行排序?
對對象數組排序,可使用Arrays.sort()方法;
對對象列表排序,可使用Collection.sort()方法。
這兩個類都有用於自然排序(使用Comparable)或基於標準的排序(使用Comparator)的重載方法sort()。Collections內部使用數組排序方法,所有它們兩者都有相同的性能,只是Collections需要花時間將列表轉換爲數組。
36.當一個集合被作爲參數傳遞給一個函數時,如何纔可以確保函數不能修改它?
集合作爲參數傳遞之前,可使用Collections.unmodifiableCollection(Collectionc)方法來創建爲只讀集合,將確保修改集合時拋出不支持修改操作的異常UnsupportedOperationException。
37.如何從給定集合那裏創建一個synchronized的集合?
我們可以使用Collections.synchronizedCollection(Collectionc)根據指定集合來獲取一個synchronized(線程安全的)集合。
38.集合框架裏實現的通用算法有哪些?
Java集合框架提供常用的算法實現,比如排序和檢索,Collections類包含這些方法實現。大部分算法是操作List的,但一部分對所有類型的集合都是可用的。部分算法有排序、搜索、混編、最大最小值。
39.大寫的O是什麼?舉幾個例子?
大寫的O描述的是,就數據結構中的一系列元素而言,一個算法的性能。Collection類就是實際的數據結構,我們通常基於時間、內存和性能,使用大寫的O來選擇集合實現。
比如:例子1:ArrayList的get(index i)是一個常量時間操作,它不依賴list中元素的數量。所以它的性能是O(1)。例子2:一個對於數組或列表的線性搜索的性能是O(n),因爲我們需要遍歷所有的元素來查找需要的元素。
40.與Java集合框架相關的有哪些最好的實踐?
(1)根據需要選擇正確的集合類型。若指定大小,選用Array而非ArrayList;若要根據插入順序遍歷一個Map,使用TreeMap。若不需要重複元素,應該使用Set。
(2)一些集合類允許指定初始容量,所以如果我們能夠估計到存儲元素的數量,我們可以使用它,就避免了重新哈希或大小調整。
(3)基於接口編程,而非基於實現編程,它允許我們後來輕易地改變實現。
(4)總是使用類型安全的泛型,避免在運行時出現ClassCastException。
(5)使用JDK提供的不可變類作爲Map的key,可以避免自己實現hashCode()和equals()。
(6)儘可能使用Collections工具類,或者獲取只讀、同步或空的集合,而非編寫自己的實現。它將會提供代碼重用性,它有着更好的穩定性和可維護性。