1. 程式人生 > 實用技巧 >Java(6)集合

Java(6)集合

一、Java集合框架概述

1、什麼是集合

  • 集合框架:用於儲存資料的容器。
    • 陣列、集合等儲存資料的結構,叫Java容器
    • 此時的儲存,是指記憶體層面的儲存,不涉及持久化的儲存。
  • 任何集合框架都包含三大塊的內容:對外的介面、介面的實現、對集合運算的演算法。

2、集合的特點

  • 陣列的特點/缺點

    • 長度固定。一旦初始化,長度不能修改。
    • 型別確定。型別嚴格(算是一個好處),當然要想可以放多種型別的資料,也可以宣告為Object型別。
    • 方法有限。新增、刪除元素效率低;獲取元素個數不方便。
    • 元素有序可重。對於無序、不重複的元素,不能滿足。
  • 集合的特點/優點

    • 容量自增長。

3、集合的體系

  • Collection介面Map介面是所有集合框架的父介面。
  • Collection介面繼承樹

說明:

  1. List介面:儲存有序、可重複的資料。“動態陣列”
  2. Set介面:儲存無序、不可重複的資料。“數學中的集合”
  • Map介面繼承樹

說明:

  1. Map介面:雙列集合,用於儲存成對的資料(key-value)。“數學中的函式”

二、Collection介面中的方法(15個)

1、說明

  • 因為Collection介面是List、Set、Queue介面的父介面,所以定義在Collection介面的中方法可以用於操作子介面的實現類的物件。

2、15個方法

方法 描述
add(Object obj) 新增元素
addAll(Collection c) 新增另一個集合中所以元素
size() 元素個數
clear() 清空集合
isEmpty() 是否為空
contains(Object obj) 包含某個元素
containsAll(Collection c) 包含某個集合中所有元素
remove(Object obj) 刪除元素
removeAll(Collection c) 差集
retainAll(Collection c) 交集
equals(Object obj) 判斷集合是否相等
toArray() 轉換為陣列
toArray(T[] a)
hashCode() 求集合的雜湊值
iterator() 返回迭代器物件

注意事項:

  • 想Collection介面的實現類物件中新增資料obj時,要求obj所在類重寫equals()

  • 陣列--->集合:Arrays.asList()

    •   List arr1 = Arrays.asList(new int[]{123, 456});
        System.out.println(arr1.size());//1
      
    •   List arr2 = Arrays.asList(new Integer[]{123, 456});
        System.out.println(arr2.size());//2
      
    • asList中是整型時,用int,預設會把陣列當場一個物件。

3、Iterator迭代器介面

  • Iterator是什麼?
    • Iterator物件成為迭代器,是設計模式的一種。主要用於遍歷集合中的元素。
    • 迭代器模式:提供一種方法訪問一個容器(container)物件中各個元 素,而又不需暴露該物件的內部細節。迭代器模式,就是為容器而生。
  • Iterator介面中方法
    • hasNext()
    • next()
    • remove()遍歷過程中移除某元素
      • Iterator可以刪除集合的元素,但是是遍歷過程中通過迭代器物件的 remove方 法,不是集合物件的remove方法。
      • 如果還未呼叫next()或在上一次呼叫 next 方法之後已經呼叫了 remove 方法, 再呼叫remove都會報IllegalStateException
  • 怎麼用Iterator遍歷集合
//用集合中的iterator方法得到迭代器物件
Iterator iterator = coll.iterator();
//遍歷集合
while(iterator.hasNext()){
    System.out.println(iterator.next());
}

//當然我們也可以用foreach來遍歷,其實foreach本質也是呼叫了迭代器。
  • Iterator原理

    • 得到的迭代器物件,預設指標指向第一個元素的前面
    • hasNext()來判斷下一位是否有元素。(在呼叫it.next()方法之前必須要呼叫it.hasNext()進行檢測。若不呼叫,且 下一條記錄無效,直接呼叫it.next()會丟擲NoSuchElementException異常)
    • next():兩個作用:①指標下移②取出指標指向的元素
  • 注意

    • 集合物件每次呼叫iterator()方法都得到一個全新的迭代器物件,預設遊標都在集合 的第一個元素之前。因此下面這種寫法不正確
while (coll.iterator().hasNext()){
    System.out.println(coll.iterator().next());
}

三、List介面

1、List介面概述

  • List介面是Collection的子介面
  • List被稱為“動態陣列”,是因為陣列的侷限性而代替陣列的一種容器。
  • 儲存的元素有序、可重複。每個元素都有對應的順序索引
  • 實現類有三個:ArrayList、LinkedList、Vector

2、ArrayList、LinkedList、Vector的異同(面試題)

    • 三個類都是List介面的實現類,因此儲存資料都是有序可重複的。
實現類 地位 since 執行緒安全否 底層 應用場景
ArrayList 主要實現類 1.2 執行緒不安全、效率高 Object[] 遍歷、查詢
LinkedList 1.2 雙向連結串列 頻繁插入、刪除
Vector 古老實現類 1.0 執行緒安全、效率低 Object[]

3、原始碼分析(加分項)

  • ArrayList原始碼分析-----JDK7

    • 建立:呼叫空參構造器時,底層建立一個長度為10的Object[]陣列elementData
    • 擴容:預設擴容為原來的1.5倍(使用移位操作),並將原來陣列中的資料複製到新的陣列中
    • 結論:開發中使用帶參構造器指定容量:ArrayList list = new ArrayList(int capacity)
  • ArrayList原始碼分析-----JDK8

    • 建立:呼叫空參構造器時,底層建立一個Object[]陣列elementData,初始化為空{}
    • 擴容:同JDK7
    • 對比:jdk7中的ArrayList的物件的建立類似於單例的餓漢式,而jdk8中的ArrayList的物件的建立類似於單例的懶漢式,延遲了陣列的建立,節省記憶體
  • LinkedList原始碼分析

    • 建立:呼叫空參構造器時,內部聲明瞭Node型別的first和last屬性,預設值為null
    • 新增:將資料封裝在Node中,建立Node物件
  • Vector原始碼分析

    • 建立:呼叫空參構造器時,底層建立一個長度為10的Object[]陣列elementData
    • 擴容:預設擴容為原來的2倍,並將原來陣列中的資料複製到新的陣列中

4、List常用方法

  • List除了從Collection集合繼承的方法外,還有一些獨有的、根據索引來操作集合元素的方法

  • 總結的簡化版(常用的、便於記憶的總結)

常用方法的作用 方法名
add(Object obj)
remove(Object obj)、remove(int index)
set(int index, Object obj)
get(int index)
add(int index, Object obj)
長度 size()
遍歷 ①、Iterator迭代器
②、增強for迴圈
③、普通for迴圈
  • 區分List中的remove方法
    • list.remove(2):刪除索引為2的,因為有remove(int index)的方法,直接匹配上不用自動裝箱就能匹配,name何必自動裝箱呢?
    • list.remove(new Integer(2)):刪除資料2,匹配的是remove(Object obj)的方法

四、Set介面

1、Set介面概述

  • Set介面是Collection的子介面。
  • Set中沒有額外的方法
  • Set中的元素不可重複,判斷是否相等用的是equals()方法
  • 實現類有HashSet、LinkedHashSet、TreeSet

2、HashSet、LinkedHashSet、TreeSet的異同

  • HashSet:是Set介面的主要實現類;執行緒不安全;可以儲存null值;
  • LinkedHashSet:是HashSet的子類;可以按照新增順序遍歷,遍歷效率高;
  • TreeSet:可以按照新增物件的指定屬性排序
    • 要求1:新增的物件型別相同
    • 要求2:物件的類要實現Comparable介面來進行自然排序,或者,實現Comparator介面來進行定製排序
      • 自然排序中,比較兩個物件是否相同的標準為(是否可新增“相同物件”):compareTo()返回0.不再是equals().
      • 定製排序中,比較兩個物件是否相同的標準為:compare()返回0.不再是equals().

3、理解無序、可重複

  • 無序
    • 指的是儲存在底層陣列的資料,並不是按照陣列索引的順序新增的,而是根據資料的雜湊值決定的。
    • 無序性,不等於隨機性
  • 不可重複
    • 相同的元素只能新增一個。元素按照equals()判斷,不能返回true。

4、新增元素的過程(以HashSet為例)

  • 新增過程並不會單獨考察,但是對於理解HashMap有很大幫助

  • 我們向HashSet中新增元素a,首先呼叫元素a所在類的hashCode()方法,計算元素a的雜湊值,此雜湊值接著通過某種演算法計算出在HashSet底層陣列中的存放位置(即為:索引位置),判斷陣列此位置上是否已經有元素:

    • 如果此位置上沒有其他元素,則元素a新增成功。 --->情況1
    • 如果此位置上有其他元素b(或以連結串列形式存在的多個元素),則比較元素a與元素b的hash值:
      • 如果hash值不相同,則元素a新增成功。--->情況2
      • 如果hash值相同,進而需要呼叫元素a所在類的equals()方法:
        • equals()返回true,元素a新增失敗
        • equals()返回false,則元素a新增成功。--->情況3
  • 對於新增成功的情況2和情況3而言:元素a 與已經存在指定索引位置上資料以連結串列的方式儲存

    • jdk 7 :元素a放到陣列中,指向原來的元素。
    • jdk 8 :原來的元素在陣列中,指向元素a
    • 總結:七上八下
  • HashSet底層:陣列+連結串列的結構。

5、重寫hashCode()和equals()方法

  • 要求:向Set(主要指:HashSet、LinkedHashSet)中新增的資料,其所在的類一定要重寫hashCode()和equals()

  • 重寫要求:重寫的hashCode()和equals()儘可能保持一致性:相等的物件必須具有相等的雜湊碼

  • 重寫兩個方法的小技巧:物件中用作 equals() 方法比較的 Field,都應該用來計算 hashCode 值。

6、面試題

  • 在List內去除重複數字值,要求儘量簡單

    • 可以用Set實現
  •   //程式的輸出結果
      HashSet set = new HashSet();
      Person p1 = new Person(1001,"AA");
      Person p2 = new Person(1002,"BB");
      
      set.add(p1); 
      set.add(p2);
      p1.name = "CC";
      set.remove(p1); 
      System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
      /*
      存放p1、p2時,根據p1和p2的屬性算出了hashCode來確定存放位置
      p1的name改為CC後,再刪除p1--->根據此時p1的屬性算hashCode來找set中和p1相同的元素,此時算出來的hashCode大概率不是p1的位置,相當於沒有找到,所以刪除並沒有成功。
      */
      
      set.add(new Person(1001,"CC"));
      System.out.println(set); //[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
      /*
      存放新建的p,是根據1001和CC來算hashCode,此時陣列中這個位置空的,所以存放成功
      */
      set.add(new Person(1001,"AA"));
      System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
      /*
      存放新建的p,先hashCode後和p1是一樣的,再equals發現不一樣,加到連結串列上,存放成功
      */
    

五、Map介面

1、Map介面概述

  • Map介面繼承樹

  • Map介面:
    • 儲存key-value的鍵值對,類似於數學中的函式
  • Map實現類對比
Map實現類 底層實現 執行緒 地位 特點
HashMap JDK7及以前:陣列+連結串列
JDK8:陣列+連結串列+紅黑樹
執行緒不安全 主要實現類 可以儲存null的k-v
LinkedHashMap 同上 同上 同上 可按照新增順序遍歷
TreeMap --- --- --- 可排序遍歷
Hashtable --- 執行緒安全 古老實現類 不可儲存null的k-v
Properties --- --- --- 常用來處理配置檔案

2、面試題

  •     HashMap的底層實現原理★★★★★(見下面第四點)
    
  •     HashMap和Hashtable的異同
    
  •     CurrentHashMap和Hashtable的異同(暫時不學)
    

3、Map結構的理解

  • Map中的key:無序的、不可重複的,使用Set儲存所有的key ---> key所在的類要重寫equals()和hashCode() (以HashMap為例);當然如果是LinkedHashMap還要實現排序介面

  • Map中的value:無序的、可重複的,使用Collection儲存所有的value --->value所在的類要重寫equals()

  • 一個鍵值對:key-value構成了一個Entry物件。

  • Map中的entry:無序的、不可重複的,使用Set儲存所有的entry

四、 HashMap的底層實現原理★★★★★(高頻面試題)

1、JDK7

  • HashMap map = new HashMap():底層建立一個長度為16的陣列Entry[] table。

  • map.put(key1, value1)

    • 呼叫key1所在類的hashCode()方法計算key1的雜湊值,用雜湊值在計算得到在Entry[]陣列中的存放位置
      • 如果此位置為空,新增成功------>情況1
      • 如果此位置不為空,比較key1和已經存在的資料的雜湊值
        • 如果key1的雜湊值和已經存在的資料的雜湊值不同,新增成功------>情況2
        • 如果key1的雜湊值和已經存在的某一個數據(key2,value2)的雜湊值相同,繼續呼叫key1所在類的equals()方法,傳入引數key2進行比較
          • 如果equals()返回值為false,新增成功------>情況3
          • 如果equals()返回值為true,用value1替換value2
  • 擴容:(當超出臨界值且要新增資料的位置不為空時)預設擴容為原來的2倍,並複製資料到新陣列

2、JDK8與JDK7的不同之處

  • HashMap map = new HashMap()時,底層沒有直接建立陣列,而是在首次put()時建立
  • 陣列是Node[]型別,而非Entry[]型別
  • JDK7底層是:陣列+連結串列;而JDK8底層是:陣列+連結串列+紅黑樹
    • 形成連結串列時,七上八下(jdk7:新的元素指向舊的元素。jdk8:舊的元素指向新的元素)
    • 當陣列的某一個索引位置上的元素以連結串列形式存在的資料個數 > 8 且當前陣列的長度 > 64時,此時此索引位置上的所資料改為使用紅黑樹儲存。

3、LinkedHashMap底層實現原理

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;//能夠記錄新增的元素的先後順序
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

六、Map介面常用方法

1、新增、刪除、修改操作:

  • 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中的所有資料

2、 元素查詢的操作:

  • 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是否相等

3、元檢視操作的方法:

  • Set keySet():返回所有key構成的Set集合
  • Collection values():返回所有value構成的Collection集合
  • Set entrySet():返回所有key-value對構成的Set集合

4、總結常用方法:

  • 新增:put(Object key,Object value)
  • 刪除:remove(Object key)
  • 修改:put(Object key,Object value)
  • 查詢:get(Object key)
  • 長度:size()
  • 遍歷:keySet() / values() / entrySet()

七、Properties

1、配置檔案

  • 預設存放路徑為當前工程下

  • 建立配置檔案的方式

    • 右鍵工程名--->new--->File:這時要寫上 .properties,如 jdbc.properties
    • 右鍵工程名--->new--->Resource Bundle:這時秩序寫名稱就會自動補全字尾
  • 存取資料時,建議使用setProperty(String key,String value)方法和 getProperty(String key)方法

八、Collections工具類

1、Collections概述

  • Collections是一個操作Set、List、Map等集合的工具類
  • Collections 中提供了一系列靜態的方法對集合元素進行排序、查詢和修改等操作, 還提供了對集合物件設定不可變、對集合物件實現同步控制等方法

2、Collections常用方法

排序操作

  • 排序操作都是針對List來講的,因為Map本身無序,何談排序
  • 返回值都是void,說明對List本身做了修改
方法 解釋
reverse(List) 反轉List中的元素
shuffle(List) 隨機排序
sort(List) 自然排序
Sort(List, Comparator) 定製排序
swap(List, int i, int j) 交換

查詢、替換

方法 解釋
Object max/min(Collection) 返回自然排序的最大、小值
Object max/min(Collection, Comparator) 返回定製排序的最大、小值
int frequency(Collection, Object) 返回某集合中某元素的出現次數
void copy(List dest, List src) 複製
boolean replaceAll(List list, Object oldVal, Object newVal) 替換
  • void copy(List dest, List src)中,新List容量不能小於舊List,因此要用 List dest = Arrays.asList(new Object[list.size()]);來建立

3、Collection和Collections的區別(面試題)