1. 程式人生 > >Java 基礎篇之集合

Java 基礎篇之集合

List 集合

List 集合中元素有序、可重複,集合中每個元素都有其對應的索引順序。

List 判斷兩個物件相等,只要通過 equals 方法比較返回 true 即可。

看個例子:

public class A {
    public boolean equals(Object obj) {
        return true;
    }
}

import java.util.ArrayList;
import java.util.List;

public class ListTest2 {
    public static void main(String[] args) {
        List books = new ArrayList();
        books.add(new String("a"));
        books.add(new String("b"));
        books.add(new String("c"));
        System.out.println(books);
        books.remove(new A());
        System.out.println(books);
        books.remove(new A());
        System.out.println(books);
    }
}

當試圖刪除一個 A 物件時,List 會呼叫 A 物件的 equals 方法依次與集合元素進行比較。如果 equals 方法以某個集合元素作為引數時返回 true,List 將會刪除該元素。這裡 A 重寫了 equals 方法,總是返回 true,所以每次都會從 List 集合中刪除一個元素。

ArrayList 類

ArrayList 類是基於陣列實現的 List 類,完全支援前面介紹的 List 介面的全部功能。

ArrayList 封裝了一個動態的、允許再分配的 Object[] 陣列。

Set 集合

HashSet 類

  • 元素沒有順序,集合元素的值可以是 null

  • HashSet 不是同步的,假設有多個執行緒同時修改了 HashSet 集合時,必須通過程式碼來保證其同步

HashSet 判斷元素相等的標準是兩個物件通過 equals() 比較相等,同時兩個物件的 hashCode()返回值也相等。

hashCode 和 equals 符合這樣一個約定:equals 返回 true, hashCode 必須相等。很多 Java 類庫中的程式碼都是按照這種約定使用這兩個方法的,比 如 HashSet。這也是為什麼我們要求如果一個類覆蓋了 hashCode 方法,就 一定要覆蓋 equals 方法,並保證方法的實現符合上述約定

當向 HashSet 集合中存入一個元素時,HashSet 會呼叫該物件的 hashCode() 方法來獲得該物件的 hashCode 值,然後根據該 hashCode 值決定該物件在 HashSet 中的儲存位置。

HashSet 中每個能儲存元素的槽位稱為桶(bucket)。如果多個元素的 hashCode 值相同,但它們通過 equals 方法比較返回 false,就需要在一個桶裡放多個元素,這會導致效能下降。所以,建議在需要把某個類的物件儲存到 HashSet 集合時,重寫該類的 equals 和 hashCode 方法,儘量保證兩個物件通過 equals 方法比較返回 true 時,他們的 hashCode 方法返回值也相等。

當把可變物件新增到 HashSet 中後,需要特別小心,儘量不要去修改可變物件中參與計算 hashCode() 、equals() 方法的例項變數,否則會導致 HashSet 無法正確訪問這些集合元素。

看個例子:

public class R {
    int count;
    public R(int count) {
        this.count = count;
    }
    public String toString() {
        return "R[count:" + count + "]";
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == R.class) {
            R r = (R)obj;
            return this.count == r.count;
        }
        return false;
    }
    public int hashCode() {
        return this.count;
    }
}

import java.util.HashSet;
import java.util.Iterator;

public class HashSetTest2 {
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        hs.add(new R(5));
        hs.add(new R(-3));
        hs.add(new R(9));
        hs.add(new R(-2));
        System.out.println(hs);
        Iterator it = hs.iterator();
        R first = (R)it.next();
        first.count = -3;
        System.out.println(hs);
        hs.remove(new R(-3));
        System.out.println(hs);
        System.out.println("hs 是否包含 count 為 -3 的 R 物件" + hs.contains(new R(-3)));
        System.out.println("hs 是否包含 count 為 -2 的 R 物件" + hs.contains(new R(-2)));
    }
}

/*
[R[count:-2], R[count:-3], R[count:5], R[count:9]]
[R[count:-3], R[count:-3], R[count:5], R[count:9]]
[R[count:-3], R[count:5], R[count:9]]
hs 是否包含 count 為 -3 的 R 物件false
hs 是否包含 count 為 -2 的 R 物件false
*/

LinkedHashSet 類

LinkedHashSet 是 HashSet 的子類,同樣根據 hashCode 值來決定元素的儲存位置。但是使用連結串列維護元素的次序,使得當遍歷 LinkedHashSet 集合裡的元素時,LinkedHashSet 會按元素的新增順序訪問集合裡的元素。

LinkedHashSet 需要維護元素的插入順序,因此效能略低於 HashSet,但在迭代訪問 Set 裡的全部元素時會有很好的效能,因為它以連結串列維護內部的順序。

TreeSet 類

TreeSet 是 SortedSet 介面的實現類,顧名思義這是一種排序的 Set 集合。

TreeSet 底層使用 TreeMap 實現,採用紅黑樹的資料結構來儲存集合元素。TreeSet 支援兩種排序方法:自然排序和定製排序。預設情況下,使用自然排序。

自然排序

Java 提供了 Comparable 介面,介面定義了一個 compareTo(Object obj) 方法。實現該介面的類必須實現該抽象方法。

compareTo(Object obj) 比較規則如下:

  • obj1.compareTo(obj2) 返回值為 0,表明相等
  • obj1.compareTo(obj2) 返回值大於 0,表明 obj1 > obj2
  • obj1.compareTo(obj2) 返回值小於 0,表明 obj1 < obj2

TreeSet 會呼叫集合元素的 compareTo(Object obj) 方法來比較元素大小關係,再將集合元素按升序排序,這就是自然排序。所以自然排序中的元素物件都必須實現了 Comparable 介面。

如果兩個物件通過 compareTo(Object obj) 比較相等, 即返回值為0,TreeSet 認為它們相等,那麼新物件將無法新增到 TreeSet 集合中。

如果希望 TreeSet 能正常工作,TreeSet 只能新增同一種類型的物件。

定製排序

如果需要實現定製排序,需要在建立 TreeSet 集合物件時,提供一個 Comparator 物件與該 TreeSet 集合關聯。Comparator 是一個函式式介面,可以使用 Lambda 表示式代替。

通過定製排序方式時,依然不可以向 TreeSet 中新增不同型別的物件,否則引發 ClassCastException 異常。此時集合判斷兩個元素相等的標準是:通過 Comparator 比較兩個元素返回了 0, 這樣 TreeSet 也不會把第二個元素新增到集合中。

import java.util.TreeSet;

public class TreeSettest4 {
    public static void main(String[] args) {
        TreeSet ts = new TreeSet((o1, o2) -> {
            M m1 = (M) o1;
            M m2 = (M) o2;
            return m1.age > m2.age ? -1 : m1.age < m2.age ? 1: 0;
        });
        ts.add(new M(5));
        ts.add(new M(-3));
        ts.add(new M(9));
        System.out.println(ts);
    }
}

上面使用目標型別為 Comparator 的 Lambda 表示式,它負責 ts 集合的排序。所有 M 類無需實現 Comparable 介面,而是由 TreeSet 關聯的 Lambda 表示式負責元素的排序。

在實現 compareTo 方法時,強烈推薦與 equals 結果一致,否則可能會出現一些奇怪的錯誤。因為有些類是根據 equals 來判斷重複性,有些是利用自然排序 x.compareTo(y) == 0 來判斷。compareTo 是判斷元素在排序中的位置是否相等,equals 是判斷元素是否相等,既然一個決定排序位置,一個決定相等,所以我們非常有必要確保當排序位置相同時,其equals也應該相等。

EnumSet 類

EnumSet 是專為列舉類設計的集合類,EnumSet 中的所有元素都必須是指定列舉型別的列舉類,該列舉型別在建立 EnumSet 時顯式或隱式的的指定。

EnumSet 的集合元素是有序的,以列舉值在 Enum 類內的定義順序來決定集合元素的順序。EnumSet 集合不允許插入 null 元素。

EnumSet 內部以位向量的形式儲存,這種儲存形式緊湊高效,佔用記憶體很小,執行效率很高。尤其是在進行批量操作時,比如呼叫 containsAll 和 retainAll 方法時。

Map 集合

定義:Map 用於儲存具有對映關係的資料,key 和 value 之間存在單向的一對一關係,key 不允許重複。

Set 與 Map 之間關係非常密切,如果把 key-value 對中的 value 當成 key 的附庸,key 在哪裡,value 就在哪裡。這樣就可以像對待 Set 一樣對待 Map 了。

實際上,Map 提供了一個 Entry 內部類來封裝 key-value 對,而計算 Entry 儲存時則只考慮 Entry 封裝的 key。從原始碼來看,Java 是先實現了 Map,然後通過包裝一個所有 value 都為 null 的 Map 就實現了 Set 集合。

HashMap 實現類

HashMap 中用作 key 的物件必須實現 hashCode() 方法和 equals() 方法。

HashMap 判斷兩個 key 相等的標準是:兩個 key 通過 equals() 方法比較返回 true,兩個 key 的 hashCode 值也相等。

HashMap 判斷兩個 value 相等的標準是:兩個物件通過 equals() 方法返回 true 即可。

與 HashSet 類似,當使用自定義類作為 HashMap 的 key 時,如果重寫該類的 equals() 方法 和 hashCode() 方法,則應該保證兩個方法的判斷標準一致,即當兩個 key 通過 equals() 方法比較返回 true 時,兩個 key 的 hashCode() 方法返回值也應該相同。

與 HashSet 類似,儘量不要使用可變物件作為 HashMap 的 key,如果使用了,則儘量不要在程式中修改作為 key 的可變物件。

LinkedHashMap 實現類

LinkedHashMap 也使用雙向連結串列來維護 key-value 對的次序(其實只需要考慮 key 的次序),該連結串列負責維護 Map 的迭代順序,迭代順序與 key-value 對的插入順序保持一致。

import java.util.LinkedHashMap;

public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap scores = new LinkedHashMap();
        scores.put("Chinses", 80);
        scores.put("English", 82);
        scores.put("Math", 76);
        scores.forEach((key ,value) -> System.out.println(key + "--->" + value));
    }
}

TreeMap 實現類

TreeMap 是一個紅黑樹資料結構,每個 key-value 對即作為紅黑樹的一個節點。TreeMap 儲存 key-value 對節點時,需要根據 key 對節點進行排序。TreeMap 可以保證所有的 key-value 對處於有序狀態。

兩種排序方式:

  • 自然排序:TreeMap 的所有 key 必須實現 Comparable 介面,而且所有的 key 應該是同一個類的物件,否則會丟擲 ClassCastException 異常
  • 定製排序:建立 TreeMap 時,傳入一個 Comparator 物件,該物件負責對 TreeMap 中的所有 key 進行排序。採用定製排序時不要求 Map 的 key 實現 Comparable 介面

TreeMap 判斷兩個 key 相等的標準是:兩個 key 通過 compareTo 方法返回 0。

類似於 TreeSet,如果使用自定義類作為 TreeMap 的 key,為了讓 TreeMap 良好的工作,則重寫該類的 equals() 方法和 compareTo() 方法時應該保持一致的結果:兩個 key 通過 equals 方法比較返回 true 時,它們通過 compareTo 方法比較應該返回 0。

在實現 compareTo 方法時,強烈推薦與 equals 結果一致,否則可能會出現一些奇怪的錯誤。因為有些類是根據 equals 來判斷重複性,有些是利用自然排序 x.compareTo(y) == 0 來判斷。compareTo 是判斷元素在排序中的位置是否相等,equals 是判斷元素是否相等,既然一個決定排序位置,一個決定相等,所以我們非常有必要確保當排序位置相同時,其equals也應該相等。

官方文件的說明:

Virtually all Java core classes that implement Comparable have natural orderings that are consistent with equals.

EnumMap 實現類

EnumMap 的 key 必須是單個列舉類的列舉值。

EnumMap 具有以下特徵:

  • EnumMap 在內部以陣列形式儲存

  • EnumMap 根據 key 的自然順序(即列舉值在列舉類中的定義順序)來維護 key-value 對的順序

  • EnumMap 不能使用 null 作為 key 值

建立 EnumMap 時必須指定一個列舉類,從而將該 EnumMap 和指定列舉類相關聯。

歡迎關注我的公眾號

相關推薦

Java 基礎集合

List 集合 List 集合中元素有序、可重複,集合中每個元素都有其對應的索引順序。 List 判斷兩個物件相等,只要通過 equals 方法比較返回 true 即可。 看個例子: public class A { public boolean equals(Object obj) {

基礎集合(一)(List)總結

intern ansi [] 集合 add 引用 public log ++ 1. List集合下常用的集合(ArrayList,LinkedList,Vector);   JVM垃圾回收GC,Java中采取了可達性分析法,標記所有從根節點開始的可達對象,未被標記的對象就

基礎集合(二)總結

線程不安全 emp abstract 和集 write next 不可變 叠代器 關系 1. Map集合和collection結合的區別 1》Collection一次存一個元素;Map一次存一對元素; 2》Collection是單列集合;Map是雙列集合; 3》Map中的存

java基礎nio與aio

sre 非阻塞 只有一個 accept ava 使用步驟 截取 city writable 1.同步和異步 同步:指一個任務運行完以後接著運行下一個任務 異步:接到一個任務後開啟一個新的線程運行此任務,基本不占用原線程時間 2.阻塞與非阻塞 阻塞:多個線程同時訪問一份數據時

Java基礎知識集合

hashcode 數據結構 false hset 自動生成 linked arraylist 相同 spa Collection集合   特點:長度可變,只能存儲引用類型,可以存儲不同的類型的元素 list   特點:元素有序(存儲和取出的順序一致),可以重復 Linke

Java基礎加強集合

依然 部分 t對象 方法返回值 需要 不可 img 技術分享 jdk 集合整體框架圖 各集合框架的概述 1. Collection(常用List和Set,不常用Queue和Vector),單元素集合。 2. Map(常用HashMap和TreeMap,不常用Has

Java基礎常量、變數、運算子

資料型別 : Java中的基本型別功能簡單,不具備物件的特性,為了使基本型別具備物件的特性,所以出現了包裝類,就可以像操作物件一樣操作基本型別資料。 基本型別對應的包裝類 基本型別 byte int short long float double boolean char 包裝型別

Java基礎順序、選擇、迴圈結構

程式流程  java程式語句執行的順序包括4種基本控制結構:順序結構、選擇結構、迴圈結構、異常處理邏輯結構。 順序結構  Java程式中,語句執行的基本順序按各語句出現的位置先後順序執行,即為順序結構。 例1:順序結構:已知三角形三邊,求三角形面積: pub

java基礎-----------抽象類 、最終類、介面

一、抽象類: 我們把一類事物抽象出來,類中只宣告方法,不實現,這就是抽象類存在的意義; 抽象類: 含有抽象方法的類,被abstract 關鍵字修飾; 抽象方法:只有方法宣告沒有方法實體,被abstract關鍵字修飾; 注意要點: 1、抽象類沒有例項物件,只能通過別的類繼承實現抽象方法

【搞定Java基礎集合類面試題整理

因為集合類在Java基礎知識中是非常重要的,也是面試中最常問到的,設計的問題也比較多,因此單獨拿出來做面試題的整理,方便自己複習,也希望給看到此篇文章的你帶來一定的幫助。文章內容均來自於網路,平時看到總結不錯的題目,就收集在此。持續更新....... 先推薦幾篇不錯的文章: 1、Java集合

Java基礎環境搭建

伺服器環境:Centos jdk安裝包:.tar.gz 第一步:在官網下載JDK安裝包 第二步:解壓縮安裝包:tar -xzvf jdk-8u131-linux-x64.tar.gz -C /usr/local/java ps:壓縮到/usr/local/java目錄

java基礎GC

概述 java和C++有著一堵 記憶體動態分配 和 垃圾收集技術 圍成的“高牆”,外面的人想進去,裡面的人想出來。 java垃圾回收 GC(Garbage Collection) 的歷史比java還要久遠,1960年誕生的Lisp語言當時就在考慮三個問題:

java基礎基礎

靜態程式碼塊執行一次,隨著類載入而載入 public class Test { static HashMap hashMap = new HashMap(){ // map優雅的寫法 { put("1","2"

java基礎5-------一維陣列的拷貝

作業: 1、{1,2,3,4,5,6} 將奇數放在偶數前面 大小順序不要求 public static int[] sortArray(int[] a) { int odd = 0;// 奇數下標 int even

Java 基礎程式設計基礎

基本資料型別 java 是強型別語言,在 java 中儲存的資料都是有型別的,而且必須在編譯時就確定其型別。 基本資料型別變數儲存的是資料本身,而引用型別變數存的是資料的空間地址。 基本型別轉換 自動型別轉換 把一個表數範圍小的數值或變數直接賦給另一個表數範圍大的變數時,系統將會進行自動型別轉換,否則需要

Java 基礎異常

異常 異常層次 Error:Java 執行時系統的內部錯誤和資源耗盡錯誤。應用程式不應該丟擲這種型別的物件。如果出現了這樣的內部錯誤,除了通告給使用者,並盡力使程式安全地終止之外,再也無能為力了。 Exception RuntimeException:由程式錯誤導致的異常 其他異常:程式本身沒有問題,

Java 基礎反射

反射 使用反射獲取程式執行時的物件和類的真實資訊。 獲取 Class 物件 每個類被載入之後,系統會為該類生成一個對應的 Class 物件,通過該 Class 物件可以訪問到 JVM 中的這個類。 使用 Class 類的 forName(String clazzName) 靜態方法。字串引數的值是某個類

java基礎第十二集合、增強for迴圈、迭代器和泛型

Collection介面中的常用方法: * 所有的子類子介面都是具有的 * 集合的方法:增刪改查 * * public boolean add(E e);//新增元素 返回值表示是否新增成功 * public boolean remove(Object o);//刪除元素,返回值表示是否刪除成

NHibernate3剖析:Mapping集合映射基礎(3):List映射

mage 專題 類名 sni adding query size top hive 系列引入 NHibernate3.0剖析系列分別從Configuration篇、Mapping篇、Query篇、Session策略篇、應用篇等方面全面揭示NHibernat

Java基礎整理Java集合

Java集合框架 常用API:     boolean addAll();     int binarySearch(List<?extends Comparable<? super T>> list, T ke