Java集合详解(超详细)
Java集合詳解(超詳細)
- 一、集合框架的概述
- 二、Collection接口
- (一)Collection接口常用方法
- (二)Iterator迭代器接口
- (三)JDK 5.0新特性--增強for循環:(foreach循環)
- 三、Collection子接口:List接口
- (一)List接口概述
- (二)List接口常用方法
- (三)實現類之一:ArrayList
- (四)實現類之一:LinkedList
- (五)實現類之一:Vector
- 四、Collection子接口:Set接口
- (一)概述
- (二)Set接口常用方法
- (三)實現類之一:HashSet
- (四)實現類之一:LinkedHashSet
- (五)實現類之一:TreeSet
- 五、Map接口
- (一)概述
- (二)Map接口常用方法
- (三)實現類之一:HashMap
- (四)實現類之一:LinkedHashMap
- (五)實現類之一:TreeMap
- (六)使用Properties讀取配置文件
- 六、Collections工具類的使用
- 七、面試題
一、集合框架的概述
集合、數組都是對多個數據進行存儲操作的結構,簡稱Java容器。 說明:此時的存儲,主要指的是內存層面的存儲,不涉及到持久化的存儲(.txt,.jpg,.avi,數據庫中)
一旦初始化以后,其長度就確定了。
數組一旦定義好,其元素的類型也就確定了。我們也就只能操作指定類型的數據了。
1.一旦初始化以后,其長度就不可修改。
2.數組中提供的方法非常限,對于添加、刪除、插入數據等操作,非常不便,同時效率不高。
3.獲取數組中實際元素的個數的需求,數組沒有現成的屬性或方法可用
4.數組存儲數據的特點:有序、可重復。對于無序、不可重復的需求,不能滿足。
Java集合可分為Collection和Map兩種體系
List:元素有序、可重復的集合
Set:元素無序、不可重復的集
其中:
二、Collection接口
- Collection接口是List、Set和Queue接口的父接口,該接口里定義的方法既可用于操作Set集合,也可用于操作List和 Queue集合。
- JDK不提供此接口的任何直接實現,而是提供更具體的子接口(如:Set和List)實現。
- 在JDK 5.0之前,Java集合會丟失容器中所有對象的數據類型,把所有對象都當成 Object類型處理;從JDK 5.0增加了泛型以后,Java集合可以記住容器中對象的數據類型。
(一)Collection接口常用方法
add(Object obj)
addAll(Collection coll)
int size()
void clear()
boolean isEmpty()
boolean contains(Object obj):是通過元素的equals方法來判斷是否是同一個對象
boolean containsAll(Collection c):也是調用元素的equals方法來比較的。用兩個兩個集合的元素逐一比較
boolean remove(Object obj):通過元素的equals方法判斷是否是要刪除的那個元素。只會刪除找到的第一個元素
boolean removeAll(Collection coll):取當前集合的差集
boolean retainAll(Collection c):把交集的結果存在當前的集合中,不影響c
boolean equals(Object obj)
Object [] toArray()
hashCode()
iterator():返回迭代器對象,用于集合遍歷
代碼實例:
@Test public void test01(){//1.add(Object e):將元素e添加到集合coll中Collection coll = new ArrayList();coll.add(123);//自動裝箱coll.add(new Date());Collection coll1 = new ArrayList();coll1.add("CC");//addAll(Collection coll1):將coll1集合中的元素添加到當前的集合中coll.addAll(coll1);//2.size():獲取添加的元素的個數System.out.println(coll.size());//3//調用collection1中的toString()方法輸出System.out.println(coll);//[123, Sun Jan 31 14:37:52 CST 2021, CC]//3.clear():清空集合元素coll.clear();//4.isEmpty():判斷當前集合是否為空System.out.println(coll.isEmpty());//true } @Test public void Test02(){Collection coll = new ArrayList();coll.add(123);coll.add(new String("Tom"));coll.add(new Person("Jerry",20));//5.contains(Object obj):判斷當前集合中是否包含obj//判斷時需要調用obj對象所在類的equals()方法System.out.println(coll.contains(new String("Tom")));//trueSystem.out.println(coll.contains(new Person("Jerry",20)));//false,重寫Person類的equals()后為true//containsAll(Collection coll1):判斷形參coll1中的所有元素是否都存在于當前集合中。Collection coll1 = Arrays.asList(1234,new String("Tom"));System.out.println(coll.containsAll(coll1));//true//6.remove(Object obj):從當前集合中移除obj元素。移除成功返回true,否則返回falseSystem.out.println(coll.remove(123));//trueSystem.out.println(coll);//[Tom, Person{name='Jerry', age=20}]//removeAll(Collection coll1):從當前集合中移除coll1中所有的元素。(差集)coll.removeAll(coll1);System.out.println(coll);//[Person{name='Jerry', age=20}]Collection a = new ArrayList();a.add(123);a.add(456);a.add(new Person("Jerry",20));a.add(new String("Tom"));a.add(false);//7.retainAll(Collection coll1):交集:獲取當前集合和coll1集合的交集,并返回給當前集合Collection b = Arrays.asList(123,456,789);System.out.println(a.retainAll(b));//trueSystem.out.println(a);//[123, 456] }@Test public void Test03(){Collection a = new ArrayList();a.add(456);a.add(new Person("Jerry",20));a.add(new String("Tom"));//8.equals(Object obj):要想返回true,需要當前集合和形參集合的元素都相同。Collection b = new ArrayList();b.add(456);b.add(new Person("Jerry",20));b.add(new String("Tom"));System.out.println(a.equals(b));//true//9.hashCode():返回當前對象的哈希值System.out.println(a.hashCode());//1350934216//10.集合 --->數組:toArray()Object[] arr = a.toArray();//拓展:數組 --->集合:調用Arrays類的靜態方法asList()List<Object> objects = Arrays.asList(arr);System.out.println(objects);//注意兩個的區別List<int[]> ints = Arrays.asList(new int[]{123, 456});System.out.println(ints.size());//1,集合將其識別為一個元素List<Integer> integers = Arrays.asList(new Integer[]{123, 456});System.out.println(integers.size());//2//11.iterator():返回Iterator接口的實例,用于遍歷集合元素。 }注意:
使用Collection集合存儲對象,要求對象所屬的類滿足: 向Collection接口的實現類的對象中添加數據obj時,要求obj所在類要重寫equals()。(二)Iterator迭代器接口
- Iterator對象稱為迭代器(設計模式的一種),主要用于遍歷 Collection 集合中的元素。
- 迭代器模式:提供一種方法訪問一個容器(container)對象中各個元素,而又不需暴露該對象的內部細節。
- Collection接口繼承了java.lang.Iterable接口,該接口有一個iterator()方法,那么所
有實現了Collection接口的集合類都有一個iterator()方法,用以返回一個實現了Iterator接口的對象。 - 集合對象每次調用iterator()方法都得到一個全新的迭代器對象,默認游標都在集合的第一個元素之前。
注意:
- 如果還未調用next()或在上一次調用 next 方法之后已經調用了 remove 方法,再調用remove都會報IllegalStateException。
- 內部定義了remove(),可以在遍歷的時候,刪除集合中的元素。此方法不同于集合直接調用remove()
代碼實例:
Iterator iterator = coll.iterator(); 刪除集合中"Tom" while(iterator.hasNext()){Object obj = iterator.next();if("Tom".equals(obj)){iterator.remove();} }(三)JDK 5.0新特性–增強for循環:(foreach循環)
三、Collection子接口:List接口
(一)List接口概述
- 概述:
鑒于Java中數組用來存儲數據的局限性,我們通常使用List替代數組
List集合類中元素有序、且可重復,集合中的每個元素都有其對應的順序索引。
List容器中的元素都對應一個整數型的序號記載其在容器中的位置,可以根據序號存取容器中的元素。
JDK AP中List接口的實現類常用的有:ArrayList、LinkedList和 Vector.
-
List接口框架
|----Collection接口:單列集合,用來存儲一個一個的對象|----List接口:存儲序的、可重復的數據。 -->“動態”數組,替換原的數組|----ArrayList:作為List接口的主要實現類;線程不安全的,效率高;底層使用Object[] elementData存儲|----LinkedList:對于頻繁的插入、刪除操作,使用此類效率比ArrayList高;底層使用雙向鏈表存儲|----Vector:作為List接口的古老實現類;線程安全的,效率低;底層使用Object[] elementData存儲
(二)List接口常用方法
- List除了從 Collection集合繼承的方法外,List集合里添加了一些根據索引來操作集合元素的方法。
| void add(int index, Object ele) | 在index位置插入ele元素 |
| boolean addAll(int index, Collection eles) | 從index位置開始將eles中的所有元素添加進來 |
| Object get(int index) | 獲取指定index位置的元素 |
| int indexOf(Object obj) | 返回obj在集合中首次出現的位置 |
| int lastIndexOf(Object obj) | 返回obj在當前集合中末次出現的位置 |
| Object remove(int index) | 移除指定index位置(0是第一個元素)的元素,并返回此元素 |
| Object set(int index, Object ele) | 設置指定index位置的元素為ele |
| List subList(int fromIndex, int toIndex) | 返回從fromIndex到toIndex位置的子集合 |
- 代碼實例:
- 遍歷的三種方式
(三)實現類之一:ArrayList
ArrayList的三個構造方法:
(1)ArrayList()構造一個初始容量為 10 的空列表。
List<String> list1 = new ArrayList<>();(2)ArrayList(int initialCapacity)構造一個具有指定初始容量的空列表。
List<String> list2 = new ArrayList<>(6);(3)ArrayList(Collection<? extends E> c)構造一個包含指定 collection 的元素的列表,這些元素是按照該 collection 的迭代器返回它們的順序排列的。
List<String> list3 = new ArrayList<>(list2);- ArrayList是List接口的典型實現類、主要實現類
- 本質上,ArrayList是對象引用的一個”變長”數組
- Array Listi的JDK 1.8之前與之后的實現區別?
JDK 1.7:ArrayList像餓漢式,直接創建一個初始容量為10的數組
JDK 1.8:ArrayList像懶漢式,一開始創建一個長度為0的數組,當添加第一個元素時再創建一個始容量為10的數組 - Arrays.asList(…)方法返回的List集合,既不是 ArrayList實例,也不是Vector實例。Arrays.asList(…)返回值是一個固定長度的List集合
(四)實現類之一:LinkedList
- 對與對于頻繁的插入和刪除元素操作,建議使用LinkedList類,效率更高
- 新增方法:
void addFirst(Object obj),在鏈表頭部插入一個元素
void addLast(Object obj),在鏈表尾部添加一個元素
Object getFirst(),獲取第一個元素
Object getlast)(),獲取最后一個元素
Object removeFirst(),刪除頭,獲取元素并刪除
Object removeLast()刪除尾 - Linkedlist:雙向鏈表,內部沒有聲明數組,而是定義了Node類型的frst和last,用于記錄首末元素。同時,定義內部類Node,作為 Linkedlist中保存數據的基本結構。Node除了保存數據,還定義了兩個變量:
prev變量記錄前一個元素的位置
next變量記錄下一個元素的位置 - LinkedList 是非線程安全的,并發環境下,多個線程同時操作 LinkedList,會引發不可預知的錯誤
(五)實現類之一:Vector
- Vector是一個古老的集合,JDK 1.0就有了。大多數操作與ArrayList相同,區別在于Vector是線程安全的
- 在各種list中,最好把ArrayList作為缺省選擇。當插入、刪除頻繁時,使用LinkedList;Vector總是比ArrayList慢,所以盡量避免選擇使用。
- JDK 7.0和JDK 8.0中通過Vector()構造器創建對象時,底層都創建了長度為10的數組。
- 在擴容方面,默認擴容為原來的數組長度的2倍。
四、Collection子接口:Set接口
(一)概述
Set接口是Collection的子接口,set接口沒有提供額外的方法
Set集合不允許包含相同的元素,如果試把兩個相同的元素加入同一個Set集合中,則添加操作失敗。(多用于過濾操作,去掉重復數據)
Set判斷兩個對象是否相同不是使用==運算符,而是根據equals()方法
常用類
|----Collection接口:單列集合,用來存儲一個一個的對象|----Set接口:存儲無序的、不可重復的數據 -->高中講的“集合”|----HashSet:作為Set接口的主要實現類;線程不安全的;可以存儲null值|----LinkedHashSet:作為HashSet的子類;遍歷其內部數據時,可以按照添加的順序遍歷,對于頻繁的遍歷操作,LinkedHashSet效率高于HashSet.|----TreeSet:可以按照添加對象的指定屬性,進行排序。HashSet使用哈希表實現的,元素是無序的。添加、刪除操作時間復雜度都是O(1)。
TreeSet內部結構是一個樹結構(紅黑樹),元素是有序的,添加、刪除操作時間復雜度為O(log(n)),并且提供了first(), last(), headSet(), tailSet()等方法來處理有序集合。
LinkedHashSet是介于HashSet 和 TreeSet之間,內部是一個雙向鏈表結構,所以它的插入是有序的,時間復雜度是O(1)。
用于存放無序的、不可重復的元素
以HashSet為例說明:
(二)Set接口常用方法
- Set接口中沒額外定義新的方法,使用的都是Collection中聲明過的方法。
(三)實現類之一:HashSet
概述:
- Hashset是Set接口的典型實現,大多數時候使用Set集合時都使用這個實現類。
- HashSet按Hash算法來存儲集合中的元素,因此具有很好的存取、查找、刪除性能。
- HashSet具有以下特點:
- HashSet集合判斷兩個元素相等的標準:兩個對象通過hashCode()方法比較相等,并且兩個對象的equals()方法返回值也相等。
- 對于存放在Set容器中的對象,對應的類一定要重寫equals()和hashCode(Object obj)方法,以實現對象相等規則。
元素添加過程:(難點)
我們向HashSet中添加元素a,首先調用元素a所在類的hashCode()方法,計算元素a的哈希值,此哈希值接著通過某種算法計算出在HashSet底層數組中的存放位置(即為:索引位置),判斷數組此位置上是否已經有元素:
equals()返回true,元素a添加失敗
equals()返回false,則元素a添加成功。—>情況3
對于添加成功的情況2和情況3而言:元素a 與已經存在指定索引位置上數據以鏈表的方式存儲。
JDK 7.0 和JDK 8.0 元素添加的區別:
(四)實現類之一:LinkedHashSet
- LinkedhashSet是HashSet的子類,是一個哈希表和鏈表的結合,且是一個雙向鏈表。
- LinkedhashSet根據元素的hashCode值來決定元素的存儲位置但它同時使用雙向鏈表 維護元素的次序,這使得元素看起來是以插入順序保存的。
- LinkedhashSet插入性能略低于HashSet,但在迭代訪問Set里的全部元素時有很好的性能。(對于頻繁的遍歷操作,LinkedHashSet效率高于HashSet)
- LinkedhashSet不允許集合元素重復。
- LinkedHashSet作為HashSet的子類,在添加數據的同時,每個數據還維護了兩個引用,記錄此數據前一個數據和后一個數據。
(五)實現類之一:TreeSet
- 繼承結構與接口實現
與HashSet集合相比,TreeSet還提供了幾個額外方法:
Comparator comparator():如果TreeSet采用了定制順序,則該方法返回定制排序所使用的Comparator,如果TreeSet采用自然排序,則返回null;
Object first():返回集合中的第一個元素;
Object last():返回集合中的最后一個元素;
Object lower(Object e):返回指定元素之前的元素。
Object higher(Object e):返回指定元素之后的元素。
SortedSet subSet(Object fromElement,Object toElement):返回此Set的子集合,含頭不含尾;
SortedSet headSet(Object toElement):返回此Set的子集,由小于toElement的元素組成;
SortedSet tailSet(Object fromElement):返回此Set的子集,由大于fromElement的元素組成;
說明:
1.向TreeSet中添加的數據,要求是相同類的對象。2.兩種排序方式:自然排序(實現Comparable接口) 和定制排序(Comparator)1)自然排序中,比較兩個對象是否相同的標準為:compareTo()返回0,不再是equals()方法2)定制排序中,比較兩個對象是否相同的標準為:compare()返回0,不再是equals()方法向TreeSet中添加的數據,要求是相同類的對象。
執行結果:會拋出一個異常:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
原因:向TreeSet中添加的數據,要求是相同類的對象
兩種排序方式
執行結果:java.lang.ClassCastException,出現了類型轉換異常
原因:在于我們需要告訴TreeSet如何來進行比較元素,如果不指定,就會拋出這個異常
解決:
- 方法一:自然排序(實現Comparable接口)
指定比較的規則,在自定義類(Person)中實現Comparable接口,并重寫接口中的compareTo方法
說明:
- 自然排序:TreeSet會調用集合元素的compareTo(object obj)方法來比較元素之間的大小關系,然后將集合元素按升序(默認情況)排列
- 如果試圖把一個對象添加到Treeset時,則該對象的類必須實現Comparable接口。
- 實現Comparable的類必須實現compareTo(Object obj)方法,兩個對象即通過compareTo(Object obj)方法的返回值來比較大小
- Comparable的典型實現:
- 向TreeSet中添加元素時,只有第一個元素無須比較compareTo()方法,后面添加的所有元素都會調用compareTo()方法進行比較。
- 因為只有相同類的兩個實例才會比較大小,所以向 TreeSet中添加的應該是同一個類的對象。 對于TreeSet集合而言,它判斷兩個對象是否相等的唯一標準是:兩個對象通過compareTo(Object obj)方法比較返回值。
- 當需要把一個對象放入TreeSet中,重寫該對象對應的equals()方法時,應保證該方法與compareTo(Object obj)方法有一致的結果:如果兩個對象通過equals()方法比較返回true,則通過compareTo(object ob)方法比較應返回0。否則,讓人難以理解。
注意:
- TreeSet會調用集合元素的compareTo(Objec obj)方法來比較元素之間的大小關系,obj1.compareTo(obj2)如果返回0表示兩個對象相等;如果返回正整數則表明obj1大于obj2,如果是負整數則相反。
代碼實例:
public class User implements Comparable{private String name;private int age;//按照姓名從大到小排列,年齡從小到大排列@Overridepublic int compareTo(Object o) {if(o instanceof User){User user = (User)o;int compare = -this.name.compareTo(user.name);if(compare != 0){return compare;}else{return Integer.compare(this.age,user.age);}}else{throw new RuntimeException("輸入的類型不匹配");}}//其他代碼省略 } public class TreeSetTest {@Testpublic void test1(){TreeSet<User> set = new TreeSet<>();set.add(new User("Tom",12));set.add(new User("Jerry",32));set.add(new User("Jack",33));set.add(new User("Jack",56));System.out.println(set);//[User{name='Tom', age=12}, User{name='Jerry', age=32}, User{name='Jack', age=33}, User{name='Jack', age=56}]} }- 方法二:定制排序(Comparator)
說明:
- TreeSet的自然排序要求元素所屬的類實現Comparable接口,如果元素所屬的類沒有實現 Comparable接口,或不希望按照升序(默認情況)的方式排列元素或希望按照其它屬性大小進行排序,則考慮使用定制排序。定制排序,通過 Comparator接口來實現。需要重寫 compare(T o1,T o2)方法。
- 利用int compare(T o1,T o2)方法,比較o1和o2的大小:如果方法返回正整數,則表示o1大于o2;如果返回0,表示相等;返回負整數,表示o1小于o2。
- 要實現定制排序,需要將實現Comparator接口的實例作為形參傳遞給TreeSet的構造器。
- 此時,仍然只能向Treeset中添加類型相同的對象。否則發生 ClassCastException異常
- 使用定制排序判斷兩個元素相等的標準是:通過 Comparator比較兩個元素返回了0
代碼實例:
@Test public void test2(){//創建一個Comparator接口的對象Comparator com = new Comparator() {//按照年齡從小到大排列@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof User && o2 instanceof User){User u1 = (User)o1;User u2 = (User)o2;return Integer.compare(u1.getAge(),u2.getAge());}else{throw new RuntimeException("輸入的數據類型不匹配");}}};//如果構造方法中沒有參數,則按照自然排序的方式進行排序//否則按照定制排序TreeSet set = new TreeSet(com);set.add(new User("Tom",12));set.add(new User("Jerry",33));set.add(new User("Jack",33));//該對象插入失敗,因為存在年齡相同的對象set.add(new User("Jack",56));System.out.println(set);//[User{name='Tom', age=12}, User{name='Jerry', age=33}, User{name='Jack', age=56}] }五、Map接口
(一)概述
- Map與Collection并列存在。用于保存具有映射關系的數據:key-value
- Map中的key和value都可以是任何引用類型的數據
- Map中的key用set來存放,不允許重復,即同一個Map對象所對應的類,須重hashCode()和 equals()方法
- 常用 String類作為Map的“鍵”
- key和value之間存在單向一對一關系,即通過指定的key總能找到唯一的、確定的value
- Map接口的常用實現類:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是Map接口使用頻率最高的實現類
-
常見類結構:
|----Map:雙列數據,存儲key-value對的數據 ---類似于高中的函數:y = f(x)|----HashMap:作為Map的主要實現類;線程不安全的,效率高;存儲null的key和value|----LinkedHashMap:保證在遍歷map元素時,可以照添加的順序實現遍歷。原因:在原的HashMap底層結構基礎上,添加了一對指針,指向前一個和后一個元素。對于頻繁的遍歷操作,此類執行效率高于HashMap。|----TreeMap:保證照添加的key-value對進行排序,實現排序遍歷。此時考慮key的自然排序或定制排序底層使用紅黑樹|----Hashtable:作為古老的實現類;線程安全的,效率低;不能存儲null的key和value(注意t小寫)|----Properties:常用來處理配置文件。key和value都是String類型HashMap的底層: 數組+鏈表 (JDK 7.0及之前)數組+鏈表+紅黑樹 (JDK 8.0以后)
-
存儲結構的理解
Map中的key:無序的、不可重復的,使用Set存儲所的key ---> key所在的類要重寫equals()和hashCode() (以HashMap為例)Map中的value:無序的、可重復的,使用Collection存儲所的value --->value所在的類要重寫equals()一個鍵值對:key-value構成了一個Entry對象。Map中的entry:無序的、不可重復的,使用Set存儲所的entry
(二)Map接口常用方法
添加、刪除、修改、查詢方法:
Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中 void putAll(Map m):將m中的所有key-value對存放到當前map中 Object remove(Object key):移除指定key的key-value對,并返回value void clear():清空當前map中的所有數據 Object get(Object key):獲取指定key對應的value boolean containsKey(Object key):是否包含指定的key boolean containsValue(Object value):是否包含指定的value int size():返回map中key-value對的個數 boolean isEmpty():判斷當前map是否為空 boolean equals(Object obj):判斷當前map和參數對象obj是否相等 @Test public void test1(){Map map = new HashMap();//1.Object put(Object key,Object value):將指定key-value添加到(或修改)當前map對象中map.put("AA",11);map.put(45,34);map.put("BB",22);//如果有相同的key,則更新valuemap.put("AA",00);System.out.println(map);//{AA=0, BB=22, 45=34}//2.void putAll(Map m):將m中的所有key-value對存放到當前map中Map map1 = new HashMap();map1.put("CC",33); map1.put("DD",44);map.putAll(map1);System.out.println(map);//{AA=0, BB=22, CC=33, DD=44, 45=34}//3.Object remove(Object key):移除指定key的key-value對,并返回value;如果沒有該key就返回nullObject value = map.remove("CC");System.out.println(value);//33//4.Object get(Object key):獲取指定key對應的valueObject obj = map.get("AA");System.out.println(obj);//0//5.boolean containsKey(Object key):是否包含指定的keySystem.out.println(map.containsKey("AA"));//trueSystem.out.println(map.containsKey("EE"));//flase//6.boolean containsValue(Object value):是否包含指定的valueSystem.out.println(map.containsValue(22));//true//7.void clear():清空當前map中的所有數據map.clear();System.out.println(map.size());//0System.out.println(map);//{}//8.boolean isEmpty():判斷當前map是否為空System.out.println(map.isEmpty());//true }元視圖操作的方法:(Map的遍歷)
Set keySet():返回所有key構成的Set集合 Collection values():返回所有value構成的Collection集合 Set entrySet():返回所有key-value對構成的Set集合 @Test public void test2(){Map map = new HashMap();map.put("AA",11);map.put("BB",22);map.put("CC",33);//方法一:Set keySet():返回所有key構成的Set集合Set set = map.keySet();Iterator iterator = set.iterator();while(iterator.hasNext()){Object obj = iterator.next();System.out.println(obj+"="+map.get(obj));//通過key來找到value}//方法二:Collection values():返回所有value構成的Collection集合,遍歷valueCollection values = map.values();for(Object obj : values){System.out.println(obj);}//方法三:Set entrySet():返回所有key-value對構成的Set集合,集合中的每個元素是Entry類型Set entrySet = map.entrySet();Iterator iterators = entrySet.iterator();while(iterators.hasNext()){Object obj = iterators.next();Map.Entry entry = (Map.Entry)obj;System.out.println(entry.getKey()+"="+entry.getValue());}//方法四:加強for循環for(Map.Entry<String,Object> entry : map.entrySet()){String mapKey = entry.getKey();Object mapValue = entry.getValue();System.out.println(mapKey+":"+mapValue);}}(三)實現類之一:HashMap
元素添加過程簡要說明:
HashMap的底層實現原理?以jdk7為例說明:
????1.HashMap map = new HashMap():
????2.在實例化以后,底層創建了長度是16的一維數組Entry[] table。
????3.map.put(key1,value1):(可能已經執行過多次put)
????4.首先,調用key1所在類的hashCode()計算key1哈希值,此哈希值經過某種算法計算以后,得到在Entry數組中的存放位置。
1)如果此位置上的數據為空,此時的key1-value1添加成功。 ----情況1
2)如果此位置上的數據不為空,(意味著此位置上存在一個或多個數據(以鏈表形式存在)),比較key1和已經存在的一個或多個數據的哈希值:
①如果key1的哈希值與已經存在的數據的哈希值都不相同,此時key1-value1添加成功。----情況2
②如果key1的哈希值和已經存在的某一個數據(key2-value2)的哈希值相同,繼續比較:調用key1所在類的equals(key2)方法,比較:
如果equals()返回false:此時key1-value1添加成功。----情況3
如果equals()返回true:使用value1替換value2。
補充:關于情況2和情況3:此時key1-value1和原來的數據以鏈表的方式存儲。
HashMap的擴容:(jdk7)
????在不斷的添加過程中,會涉及到擴容問題,當超出臨界值(且要存放的位置非空)時,擴容。默認的擴容方式:擴容為原來容量的2倍,并將原有的數據復制過來。
????當HashMap中的元素越來越多的時候,hash沖突的幾率也就越來越高,因為數組的長度是固定的。所以為了提高查詢的效率,就要對 HashMap的數組進行擴容,而在HashMap數組擴容之后,原數組中的數據必須重新計算其在新數組中的位置,并放進去,這就是 resize。
HashMap擴容時機:(jdk7)
????當HashMap中的元素個數超過數組大小(數組總大小 length,不是數組中個數)* loadFactor時,就會進行數組擴容,loadFactor的默認值(DEFAULT_LOAD_ FACTOR)為0.75,這是一個折中的取值。也就是說,默認情況下,數組大小(DEFAULT INITIAL CAPACITY)為16,那么當 HashMap中元素個數超過16 * 0.75=12(這個值就是代碼中的 threshold值,也叫做臨界值)的時候,就把數組的大小擴展為2 * 16=32,即擴大一倍,然后重新計算每個元素在數組中的位置,而這是一個非常消耗性能的操作,所以如果我們已經預知 HashMap中元素的個數,那么預設元素的個數能夠有效的提高HashMap的性能。
HashMap在JDK 8.0底層實現原理:
HashMap添加元素的過程:(jdk8)
????當實例化一個HashMap時,會初始化 initialCapacity和loadFactor,在put第一對映射關系時,系統會創建一個長度為 initialCapacity的Node數組,這個長度在哈希表中被稱為容量(Capacity),在這個數組中可以存放元素的位置我們稱之為“桶”( bucket),每個bucket都有自己的索引,系統可以根據索引快速的查找bucket中的元素。
????每個 bucket中存儲一個元素,即一個Node對象,但每一個Noe對象可以帶個引用變量next,用于指向下一個元素,因此,在一個桶中,就有可能生成一個Node鏈。也可能是一個一個 TreeNode對象,每一個Tree node對象可以有兩個葉子結點left和right,因此,在一個桶中,就有可能生成一個TreeNode樹。而新添加的元素作為鏈表的last,或樹的葉子結點。
HashMap的擴容機制:(jdk8)
????當HashMapl中的其中一個鏈的對象個數沒有達到8個和JDK 7.0以前的擴容方式一樣。
當HashMapl中的其中一個鏈的對象個數如果達到了8個,此時如果 capacity沒有達到64,那么HashMap會先擴容解決,如果已經達到了64,那么這個鏈會變成樹,結點類型由Node變成 Tree Node類型。當然,如果當映射關系被移除后,下次resize方法時判斷樹的結點個數低于6個,也會把樹再轉為鏈表。
jdk8 相較于jdk7在底層實現方面的不同:
????1.new HashMap():底層沒有創建一個長度為16的數組
????2. jdk 8底層的數組是:Node[],而非Entry[]
????3. 首次調用put()方法時,底層創建長度為16的數組
????4. jdk7底層結構只有:數組+鏈表。jdk8中底層結構:數組+鏈表+紅黑樹。
????1)形成鏈表時,七上八下(jdk7:新的元素指向舊的元素。jdk8:舊的元素指向新的元素)
????2)當數組的某一個索引位置上的元素以鏈表形式存在的數據個數 > 8 且當前數組的長度 64時,此時此索引位置上的所數據改為使用紅黑樹存儲。(方便查找)
HashMap底層典型屬性的說明:
????DEFAULT_INITIAL_CAPACITY : HashMap的默認容量,16
????DEFAULT_LOAD_FACTOR:HashMap的默認加載因子:0.75
????threshold:擴容的臨界值,= 容量*填充因子:16 * 0.75 => 12
????TREEIFY_THRESHOLD:Bucket中鏈表長度大于該默認值,轉化為紅黑樹:JDK 8.0引入
????MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量:64
(四)實現類之一:LinkedHashMap
LinkedHashMap底層使用的結構與HashMap相同,因為LinkedHashMap繼承于HashMap.
區別就在于:LinkedHashMap內部提供了Entry,替換HashMap中的Node.
與LinkedhashSet類似,LinkedHashMap可以維護Map的迭代順序:迭代順序與Key-value對的插入順序一致
(五)實現類之一:TreeMap
????1.TreeMap存儲Key-Value對時,需要根據key-value對進行排序。TreeMap可以保證所有的 Key-Value對處于有序狀態。
????2.TreeSet底層使用紅黑樹結構存儲數據
????3.TreeMap的Key的排序:
TreeMap判斷兩個key相等的標準:兩個key通過 compareTo()方法或者compare()方法返回0.
代碼示例:
class User implements Comparable{String name;int age;public User() {}public User(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';}//按照姓名從大到小排列,年齡從小到大排列@Overridepublic int compareTo(Object o) {if(o instanceof User){User user = (User)o;int compare = -this.name.compareTo(user.name);if(compare == 0){ // return this.age-user.age;return Integer.compare(this.age,user.age);}else{return compare;}}throw new RuntimeException("輸入的類型不匹配");}} public class TreeMapTest {//方式二:自然排序@Testpublic void test1() {TreeMap map = new TreeMap();User u1 = new User("Tom",23);User u2 = new User("Jerry",32);User u3 = new User("Tom",20);User u4 = new User("Rose",18);map.put(u1,98);map.put(u2,89);map.put(u3,76);map.put(u4,100);System.out.println(map);}//方式一:定制排序@Testpublic void test2(){Comparator com = new Comparator(){//按照年齡升序排序@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof User && o2 instanceof User){User user1 = (User)o1;User user2 = (User)o2;return Integer.compare(user1.age,user2.age);}throw new RuntimeException("輸入類型不匹配");}};TreeMap map = new TreeMap(com);User u1 = new User("Tom",23);User u2 = new User("Jerry",32);User u3 = new User("Tom",20);User u4 = new User("Rose",18);map.put(u1,98);map.put(u2,89);map.put(u3,76);map.put(u4,100);System.out.println(map);} }(六)使用Properties讀取配置文件
Hashtable的介紹:
Hashtable是個古老的Map實現類,JDK1.0就提供了。不同于 HashMap,Hashtable是線程安全的.
Hashtable實現原理和HashMap相同,功能相同。底層都使用哈希表結構,查詢速度快,很多情況下可以互用
與HashMap.不同,Hashtable不允許使用null作為key和value.
與HashMap一樣,Hashtable也不能保證其中Key-value對的順序.
Hashtable判斷兩個key相等、兩個value相等的標準,與HashMap-致.
Properties類是Hashtable的子類,該對象用于處理屬性文件
由于屬性文件里的key、value都是字符串類型,所以Properties里的key和value都是字符串類型
存取數據時,建議使用setProperty(String key,String value)方法和getProperty(String key)方法
代碼實例:
出現中文亂碼的解決辦法:
使用Classloader加載src目錄下的配置文件
//Properties:用來讀取配置文件 @Test public void test2() throws Exception {Properties pro = new Properties();//讀取配置文件方式一:此時的文件默認在當前的module下 // FileInputStream fis1 = new FileInputStream(new File("jdbc1.properties")); // pro.load(fis1);//讀取配置文件方式二:使用ClassLoader//配置文件默認識別為:當前module的src下ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();//獲取系統類加載器InputStream fis2 = classLoader.getResourceAsStream("jdbc1.properties");pro.load(fis2);String user = pro.getProperty("user");String password = pro.getProperty("password");System.out.println("user="+user+",password="+password); }六、Collections工具類的使用
1. 作用:
Collections是一個操作Set、List和Map等集合的工具類
Collections中提供了一系列靜態的方法對集合元素進行排序、査詢和修改等操作,還提供了對集合對象設置不可變、對集合對象實現同步控制等方法
常用方法:
排序操作:
查找、替換操作:
Object max(Collection):根據元素的自然順序,返回給定集合中的最大元素Object max(Collection,Comparator):根據 Comparator 指定的順序,返回給定集合中的最大元素Object min(Collection)Object min(Collection,Comparator)int frequency(Collection,Object):返回指定集合中指定元素的出現次數void copy(List dest,List src):將src中的內容復制到dest中boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替換 List 對象的所有舊值代碼實例:
Collections 類中提供了多個 synchronizedXxx() 方法,該方法可使將指定集合包裝成線程同步的集合,從而可以解決多線程并發訪問集合時的線程安全問題
代碼實例:
七、面試題
ArrayList和 Linkedlist的異同:
二者都線程不安全,相比線程安全的 Vector,ArrayList執行效率高。 此外,ArrayList是實現了基于動態數組的數據結構,Linkedlist基于鏈表的數據結構。對于隨機訪問get和set,ArrayList覺得優于Linkedlist,因為Linkedlist要移動指針。對于新增和刪除操作add(特指插入)和 remove,Linkedlist比較占優勢,因為 ArrayList要移動數據。
ArrayList和 Vector的區別:
Vector和ArrayList幾乎是完全相同的,唯一的區別在于Vector是同步類(synchronized),屬于強同步類。因此開銷就比 ArrayList要大,訪問要慢。正常情況下,大多數的Java程序員使用ArrayList而不是Vector,因為同步完全可以由程序員自己來控制。Vector每次擴容請求其大小的2倍空間,而ArrayList是1.5倍。Vector還有一個子類Stack.
集合Collection中存儲的如果是自定義類的對象,需要自定義類重寫哪個方法?
List:equals()方法,add()方法不需要調用equals(),主要用于contains()/remove()/retainsAll()...等方法。Set: (HashSet、LinkedHashSet):equals()、hashCode()(TreeSet):comparable:compareTo(Object obj):comparator:compare(Object o1,Object o2)關于hashSet()存儲相關的代碼分析
????負載因子的大小決定了HashMap的數據密度。
????負載因子越大密度越大,發生碰撞的幾率越高,數組中的鏈表越容易長,造成査詢或插入時的比較次數增多,性能會下降
????負載因子越小,就越容易觸發擴容,數據密度也越小,意味著發生碰撞的幾率越小,數組中的鏈表也就越短,查詢和插入時比較的次數也越小,性能會更高。但是會浪費一定的內容空間。而且經常擴容也會影響性能,建議初始化預設大一點的空間
????按照其他語言的參考及研究經驗,會考慮將負載因子設置為0.7~0.75,此時平均檢索長度接近于常數。
好的博客:
Java集合類(四)—TreeSet
關于紅黑樹(R-B tree)原理
總結
以上是生活随笔為你收集整理的Java集合详解(超详细)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 山西DotNet俱乐部网站改版成功
- 下一篇: jsp论坛网站模版_网站关键词优化怎么做