最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JAVA基礎(chǔ)之?dāng)?shù)組和集合區(qū)別對比分析

 更新時間:2025年11月01日 09:56:11   作者:王依華  
文章主要介紹了Java中數(shù)組和集合的基本概念、使用方法以及它們之間的區(qū)別,文章還探討了不可變集合的創(chuàng)建方式及其線程安全和不可篡改的優(yōu)勢,感興趣的朋友跟隨小編一起看看吧

一、數(shù)組:固定長度的連續(xù)存儲容器

數(shù)組是 Java 中最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)之一,適用于存儲固定長度、相同數(shù)據(jù)類型的元素,在內(nèi)存中占據(jù)連續(xù)空間,支持通過索引快速訪問。

1.1 數(shù)組的核心特性

  • 長度不可變:數(shù)組在創(chuàng)建時必須顯式指定長度(如int[] arr = new int[5]),一旦創(chuàng)建,長度無法動態(tài)修改。若需增減元素,需手動創(chuàng)建新數(shù)組并復(fù)制原元素。
  • 元素類型統(tǒng)一:數(shù)組中所有元素必須是同一數(shù)據(jù)類型,可分為兩類:
    • 基本數(shù)據(jù)類型數(shù)組:存儲具體數(shù)值(如int[]、double[]),默認(rèn)值為對應(yīng)基本類型的零值(int為 0,double為 0.0,char'\u0000')。
    • 引用數(shù)據(jù)類型數(shù)組:存儲對象的引用(如String[]、Student[]),默認(rèn)值為null
  • 連續(xù)內(nèi)存存儲:數(shù)組元素在內(nèi)存中按順序連續(xù)排列,索引(從 0 開始)直接對應(yīng)元素在內(nèi)存中的偏移量,因此訪問任意元素的時間復(fù)雜度為O(1),查詢效率極高。
  • 默認(rèn)值初始化:無論是否顯式賦值,數(shù)組創(chuàng)建后都會自動初始化所有元素為對應(yīng)類型的默認(rèn)值,避免空指針或垃圾值問題。

1.2 數(shù)組的聲明與初始化

數(shù)組的初始化分為 “動態(tài)初始化”(先指定長度,后賦值)和 “靜態(tài)初始化”(直接指定元素,長度由元素個數(shù)決定),兩種方式不可同時使用。

// 1. 動態(tài)初始化:指定長度,元素為默認(rèn)值
int[] arr1 = new int[3]; // 長度3,元素默認(rèn)值為0、0、0
arr1[0] = 10; // 手動賦值第一個元素
// 2. 靜態(tài)初始化:指定元素,長度自動為3
int[] arr2 = new int[]{10, 20, 30}; 
// 簡化寫法(僅聲明時可用)
int[] arr3 = {10, 20, 30}; 
// 3. 引用數(shù)據(jù)類型數(shù)組初始化
String[] strArr = new String[2]; // 默認(rèn)值為[null, null]
strArr[0] = "Java"; // 賦值第一個元素為字符串對象

1.3 多維數(shù)組

多維數(shù)組本質(zhì)是 “數(shù)組的數(shù)組”,最常用的是二維數(shù)組,適用于存儲表格類數(shù)據(jù)(如矩陣)。

  • 二維數(shù)組的初始化
// 1. 規(guī)則二維數(shù)組(每行長度相同)
int[][] matrix1 = new int[2][3]; // 2行3列,元素默認(rèn)值為0
matrix1[0][1] = 5; // 給第1行第2列元素賦值
// 2. 不規(guī)則二維數(shù)組(每行長度可不同)
int[][] matrix2 = new int[2][]; // 先指定行數(shù),不指定列數(shù)
matrix2[0] = new int[3]; // 第1行長度為3
matrix2[1] = new int[2]; // 第2行長度為2
  • 二維數(shù)組的遍歷:需通過嵌套循環(huán),外層遍歷 “行數(shù)組”,內(nèi)層遍歷 “行中的元素”。
for (int i = 0; i < matrix2.length; i++) { // 遍歷行
    for (int j = 0; j < matrix2[i].length; j++) { // 遍歷每行的元素
        System.out.print(matrix2[i][j] + " ");
    }
    System.out.println();
}

1.4 Arrays 工具類

Java 提供java.util.Arrays類,封裝了數(shù)組的常用操作(排序、查找、填充等),無需手動實現(xiàn)復(fù)雜邏輯。

方法名功能描述示例
sort(數(shù)組)對數(shù)組進(jìn)行升序排序(基本類型用快速排序,引用類型用 TimSort)Arrays.sort(arr1);(排序 int 數(shù)組)
binarySearch(數(shù)組, 目標(biāo)值)二分查找目標(biāo)值在有序數(shù)組中的索引,未找到返回負(fù)數(shù)int index = Arrays.binarySearch(arr2, 20);
fill(數(shù)組, 填充值)將數(shù)組所有元素替換為指定填充值Arrays.fill(arr1, 5);(將 arr1 所有元素設(shè)為 5)
toString(數(shù)組)將數(shù)組轉(zhuǎn)為字符串(如[10, 20, 30]),方便打印System.out.println(Arrays.toString(arr2));
copyOf(原數(shù)組, 新長度)復(fù)制原數(shù)組,新數(shù)組長度為指定值,超出部分用默認(rèn)值填充int[] newArr = Arrays.copyOf(arr2, 5);(新數(shù)組長度 5,后 2 個元素為 0)

1.5 數(shù)組的使用場景與局限性

  • 適用場景
    • 存儲長度固定、需頻繁查詢的數(shù)據(jù)。
    • 底層實現(xiàn)其他數(shù)據(jù)結(jié)構(gòu)。
  • 局限性
    • 長度固定,無法動態(tài)增減元素,增刪操作需手動處理數(shù)組復(fù)制,效率低(時間復(fù)雜度 O (n))。
    • 僅支持索引訪問,無內(nèi)置的增刪改查方法,需手動實現(xiàn)(如判斷元素是否存在、刪除指定元素)。
    • 無法直接存儲不同數(shù)據(jù)類型的元素(如需存儲多種類型,需用Object[],但會丟失類型安全性)。 

二、集合:動態(tài)長度的靈活存儲框架

集合是 Java 為解決數(shù)組局限性設(shè)計的動態(tài)數(shù)據(jù)結(jié)構(gòu),支持自動擴(kuò)容、內(nèi)置增刪改查方法,且僅存儲引用數(shù)據(jù)類型(基本類型需通過包裝類存儲,如int對應(yīng)Integer)。

集合框架體系
├─ Collection(單列集合:存儲單個元素)
│  ├─ List(有序、可重復(fù)、有索引)
│  │  ├─ ArrayList(底層數(shù)組,查詢快、增刪慢)
│  │  └─ LinkedList(底層雙向鏈表,增刪快、查詢慢)
│  └─ Set(無序、不可重復(fù)、無索引)
│     ├─ HashSet(底層哈希表,增刪查快,無序)
│     └─ TreeSet(底層紅黑樹,自動排序,有序)
└─ Map(雙列集合:存儲鍵值對Key-Value)
   ├─ HashMap(底層哈希表,無序、鍵唯一,允許null鍵/值)
   └─ TreeMap(底層紅黑樹,按鍵排序,有序,不允許null鍵)

2.1 Collection 接口:單列集合的頂層規(guī)范

Collection是所有單列集合的父接口,定義了單列集合的通用方法,所有實現(xiàn)類(如ArrayList、HashSet)都需遵守這些規(guī)范。

2.1.1 Collection 的通用方法

方法名功能描述示例代碼
boolean add(E e)向集合添加元素,成功返回true,失?。ㄈ?Set 重復(fù))返回falseList<String> list = new ArrayList<>(); list.add("Java");
boolean remove(Object o)刪除集合中指定元素,成功返回true,無此元素返回falselist.remove("Java");
boolean removeIf(Predicate filter)按條件刪除元素(Java 8+),過濾邏輯由Predicate接口實現(xiàn)list.removeIf(s -> s.length() > 5);(刪除長度 > 5 的元素)
void clear()清空集合中所有元素,集合變?yōu)榭眨ㄩL度 0)list.clear();
boolean contains(Object o)判斷集合是否包含指定元素,包含返回trueboolean hasJava = list.contains("Java");
boolean isEmpty()判斷集合是否為空(長度 0),空返回trueboolean isEmpty = list.isEmpty();
int size()返回集合中元素的個數(shù)(長度)int count = list.size();
Object[] toArray()將集合轉(zhuǎn)為數(shù)組,方便兼容數(shù)組操作Object[] arr = list.toArray();

2.1.2 Collection 的遍歷方式

遍歷是集合的核心操作,Collection提供 3 種常用遍歷方式,適用于不同場景:

方式 1:迭代器(Iterator)—— 支持遍歷中刪除元素

迭代器是Collection的內(nèi)置遍歷工具,通過iterator()方法獲取,支持在遍歷過程中安全刪除元素(避免ConcurrentModificationException異常)。

List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
// 1. 獲取迭代器對象
Iterator<String> iterator = list.iterator();
// 2. 遍歷:hasNext()判斷是否有下一個元素,next()獲取元素并移動指針
while (iterator.hasNext()) {
    String element = iterator.next();
    if ("banana".equals(element)) {
        // 迭代器的remove()方法:刪除當(dāng)前遍歷到的元素
        iterator.remove(); 
    }
    System.out.println(element); // 輸出apple、banana、cherry(刪除后集合中無banana)
}
方式 2:增強(qiáng) for 循環(huán)(for-each)—— 簡潔的遍歷

增強(qiáng) for 循環(huán)是 Java 5 引入的簡化語法,底層基于迭代器實現(xiàn),適用于 “僅遍歷,不修改集合結(jié)構(gòu)” 的場景,代碼簡潔易讀。

// 語法:for (元素類型 變量名 : 集合/數(shù)組)
for (String element : list) {
    System.out.println(element); // 輸出apple、cherry(已刪除banana)
}
方式 3:Lambda 表達(dá)式 + forEach(Java 8+)—— 函數(shù)式遍歷

Java 8 為Collection新增forEach()方法,支持通過 Lambda 表達(dá)式傳遞遍歷邏輯,代碼更簡潔,適合函數(shù)式編程風(fēng)格。

// 語法:collection.forEach(元素 -> 遍歷邏輯)
list.forEach(element -> {
    if (element.startsWith("a")) { // 篩選以"a"開頭的元素
        System.out.println(element); // 輸出apple
    }
});

2.1.3 List 接口:有序可重復(fù)的單列集合

ListCollection的子接口,特點是有序(存儲與取出順序一致)、可重復(fù)(允許元素值相同)、有索引(支持通過索引訪問元素),適用于需要 “按順序存儲、可通過位置操作” 的場景(如購物車、任務(wù)列表)。

2.1.3.1 List 的核心特性
  • 索引支持:可通過索引(0 開始)訪問、修改、刪除元素(如get(0)獲取第一個元素,set(1, "new")修改第二個元素)。
  • 元素可重復(fù):允許添加多個值相同的元素(如list.add("Java"); list.add("Java"),集合中會存在兩個 "Java")。
  • 有序性:元素的存儲順序與取出順序完全一致(如按A、B、C的順序添加,遍歷也會按A、B、C返回)。
2.1.3.2 ArrayList實現(xiàn)類:數(shù)組實現(xiàn)的高效查詢集合

ArrayList底層基于動態(tài)數(shù)組實現(xiàn),默認(rèn)初始容量為 10,當(dāng)元素個數(shù)超過閾值(容量 × 負(fù)載因子 0.75)時,會自動擴(kuò)容為原容量的 1.5 倍(如 10→15→22...),適合頻繁查詢、少量增刪的場景。

(1)ArrayList 的構(gòu)造方法

構(gòu)造方法功能描述示例
ArrayList()創(chuàng)建默認(rèn)初始容量為 10 的空集合List<String> list = new ArrayList<>();
ArrayList(int initialCapacity)創(chuàng)建指定初始容量的空集合(避免頻繁擴(kuò)容)List<String> list = new ArrayList<>(20);(初始容量 20)
ArrayList(Collection<? extends E> c)將其他 Collection 集合轉(zhuǎn)為 ArrayListSet<String> set = new HashSet<>(); List<String> list = new ArrayList<>(set);

(2)ArrayList 的核心方法

索引相關(guān)操作

List<String> list = new ArrayList<>();
list.add("apple"); // 末尾添加元素
list.add(1, "banana"); // 索引1處插入元素(原元素后移)
String first = list.get(0); // 獲取索引0的元素(apple)
list.set(0, "orange"); // 修改索引0的元素為orange
String removed = list.remove(1); // 刪除索引1的元素(banana),返回被刪除元素

(3)ArrayList 的性能分析

  • 查詢效率:通過索引直接訪問元素,時間復(fù)雜度O(1),效率極高。
  • 增刪效率
    • 末尾增刪:直接操作數(shù)組末尾,時間復(fù)雜度O(1)
    • 中間增刪:需移動后續(xù)元素(如在索引 1 插入元素,需移動索引 1 及之后的所有元素),時間復(fù)雜度O(n),效率低。
  • 擴(kuò)容機(jī)制:擴(kuò)容時需創(chuàng)建新數(shù)組并復(fù)制原元素,頻繁擴(kuò)容會消耗性能,因此建議提前估算元素個數(shù),通過ArrayList(int initialCapacity)指定初始容量。
2.1.3.3 LinkedList實現(xiàn)類:鏈表實現(xiàn)的高效增刪集合

LinkedList底層基于雙向鏈表實現(xiàn),每個元素(節(jié)點)包含 “前驅(qū)節(jié)點引用、自身值、后繼節(jié)點引用”,無需連續(xù)內(nèi)存空間,適合頻繁增刪、少量查詢的場景(如隊列、棧)。

(1)LinkedList 的特有方法(操作首尾元素)

由于鏈表結(jié)構(gòu)的特性,LinkedList提供了直接操作首尾元素的方法,時間復(fù)雜度均為O(1)

LinkedList<String> list = new LinkedList<>();
list.addFirst("a"); // 鏈表開頭添加元素
list.addLast("b"); // 鏈表末尾添加元素
String first = list.getFirst(); // 獲取開頭元素(a)
String last = list.getLast(); // 獲取末尾元素(b)
String removedFirst = list.removeFirst(); // 刪除并返回開頭元素(a)
String removedLast = list.removeLast(); // 刪除并返回末尾元素(b)

(2)LinkedList 的性能分析

  • 增刪效率
    • 首尾增刪:直接修改首尾節(jié)點的引用,時間復(fù)雜度O(1),效率極高。
    • 中間增刪:需先通過遍歷找到目標(biāo)節(jié)點(時間復(fù)雜度 O (n)),再修改節(jié)點引用(O (1)),整體效率低于首尾操作。
  • 查詢效率:無索引,查詢指定元素需從鏈表頭 / 尾開始遍歷,時間復(fù)雜度O(n),效率低。
2.1.3.4 ArrayList 與 LinkedList 的對比選擇
對比維度ArrayListLinkedList
底層結(jié)構(gòu)動態(tài)數(shù)組雙向鏈表
查詢效率高(O (1))低(O (n))
首尾增刪低(O (n),需擴(kuò)容 / 移動元素)高(O (1))
中間增刪低(O (n),需移動元素)中(O (n),需遍歷找節(jié)點)
內(nèi)存占用連續(xù)內(nèi)存,可能有空閑空間(擴(kuò)容預(yù)留)非連續(xù)內(nèi)存,每個節(jié)點需存儲前后引用,內(nèi)存開銷略大
適用場景頻繁查詢、少量增刪(如商品列表、數(shù)據(jù)展示)頻繁首尾增刪(如隊列、棧)、少量查詢

2.1.4 Set 接口:無序不可重復(fù)的單列集合

SetCollection的子接口,特點是無序(存儲與取出順序可能不一致)、不可重復(fù)(元素值唯一)、無索引,適用于需要 “去重” 的場景(如用戶 ID 列表、不重復(fù)的標(biāo)簽)。

2.1.4.1 Set 的核心特性(補(bǔ)充 Collection)
  • 元素不可重復(fù):添加重復(fù)元素時,add()方法返回false,集合不會存儲重復(fù)值(去重邏輯由具體實現(xiàn)類決定)。
  • 無索引:不支持通過索引訪問元素,因此無法使用普通for循環(huán)遍歷,只能用迭代器、增強(qiáng)forforEach。
  • 無序性
    • HashSet:完全無序,元素存儲順序與添加順序無關(guān)。
    • TreeSet:有序,會按元素的 “自然順序” 或 “自定義比較器順序” 排序。
2.1.4.2 HashSet:哈希表實現(xiàn)的高效去重集合

 HashSet 判斷元素是否重復(fù)的過程如下:

HashSet底層基于哈希表(數(shù)組 + 鏈表 / 紅黑樹) 實現(xiàn),是Set最常用的實現(xiàn)類,特點是增刪查效率高、完全無序、支持 null 元素。

(1)HashSet 的去重原理:hashCode () + equals ()

HashSet判斷元素是否重復(fù)的核心是 “先比哈希值,再比內(nèi)容”,需依賴元素的hashCode()equals()方法,具體流程如下:

  1. 調(diào)用新增元素的hashCode()方法,計算其哈希值,根據(jù)哈希值確定在哈希表中的 “桶位置”(數(shù)組索引)。
  2. 若該桶位置為空,直接將元素存入(無重復(fù))。
  3. 若該桶位置不為空(哈希沖突),則調(diào)用元素的equals()方法,與桶中已有的元素逐一比較:
    • equals()返回true:元素重復(fù),不存入。
    • equals()返回false:元素不重復(fù),將元素存入桶中(JDK 8 + 中,若桶中元素超過 8 個,鏈表會轉(zhuǎn)為紅黑樹,提升查詢效率)。

因此,為了確保 HashSet 能夠正確判斷元素的唯一性,需要重寫元素類的 hashCode() 和 equals() 方法。

(2)關(guān)鍵注意事項

  • 若自定義類(如StudentBook)的對象要存入HashSet,必須重寫hashCode()equals()方法,否則會默認(rèn)使用Object類的方法(hashCode()返回對象地址,equals()比較地址),導(dǎo)致無法正確去重。
  • 重寫規(guī)則:
    • 若兩個對象equals()返回true,則它們的hashCode()必須相等。
    • 若兩個對象hashCode()不相等,則equals()必須返回false(減少哈希沖突)。

(3)HashSet 的構(gòu)造方法與常用方法

// 1. 構(gòu)造方法
HashSet<String> set1 = new HashSet<>(); // 默認(rèn)初始容量16,負(fù)載因子0.75
HashSet<String> set2 = new HashSet<>(20); // 指定初始容量
Set<String> temp = new ArrayList<>();
HashSet<String> set3 = new HashSet<>(temp); // 從其他Collection轉(zhuǎn)換
// 2. 常用方法(與Collection一致,無特有方法)
set1.add("Java");
set1.add("Python");
set1.add("Java"); // 重復(fù)元素,add()返回false,集合中僅存1個"Java"
boolean hasPython = set1.contains("Python"); // true
set1.remove("Python"); // 刪除元素,返回true
int size = set1.size(); // 1
2.1.4.3 TreeSet:紅黑樹實現(xiàn)的有序去重集合

TreeSet底層基于紅黑樹(一種自平衡二叉搜索樹) 實現(xiàn),特點是自動排序、元素不可重復(fù)、不支持 null 元素,適用于需要 “去重且排序” 的場景(如按價格排序的商品列表、按學(xué)號排序的學(xué)生列表)。

(1)TreeSet 的排序方式

TreeSet的排序依賴 “比較邏輯”,分為兩種方式:

自然排序(默認(rèn)):元素類需實現(xiàn)Comparable接口,并重寫compareTo()方法,定義元素的排序規(guī)則。

// 自定義Student類,實現(xiàn)Comparable接口,按學(xué)號升序排序
class Student implements Comparable<Student> {
    private int id;
    private String name;
    // 構(gòu)造方法、getter/setter省略
    @Override
    public int compareTo(Student other) {
        // 按id升序:當(dāng)前id - 其他id,返回正數(shù)則當(dāng)前元素在后,負(fù)數(shù)在前
        return this.id - other.id; 
    }
}
// 使用TreeSet存儲Student,自動按id升序排序
TreeSet<Student> studentSet = new TreeSet<>();
studentSet.add(new Student(3, "Alice"));
studentSet.add(new Student(1, "Bob"));
studentSet.add(new Student(2, "Charlie"));
// 遍歷輸出:Bob(id=1)、Charlie(id=2)、Alice(id=3)
for (Student s : studentSet) {
    System.out.println(s.getName());
}

自定義排序:若元素類無法修改(如String、Integer),或需臨時改變排序規(guī)則,可在創(chuàng)建TreeSet時傳入Comparator接口實現(xiàn)類(或 Lambda 表達(dá)式)。

// 存儲String,按字符串長度降序排序(自定義比較器)
TreeSet<String> strSet = new TreeSet<>((s1, s2) -> {
    // 按長度降序:s2長度 - s1長度
    return s2.length() - s1.length(); 
});
strSet.add("apple"); // 5個字符
strSet.add("banana"); // 6個字符
strSet.add("pear"); // 4個字符
// 遍歷輸出:banana(6)、apple(5)、pear(4)
for (String s : strSet) {
    System.out.println(s);
}

(2)TreeSet 的關(guān)鍵注意事項

  • 排序邏輯決定去重:TreeSet判斷元素是否重復(fù)的依據(jù)是 “compareTo()compare()方法返回 0”,若返回 0,認(rèn)為元素重復(fù),不存入。
  • 不支持 null 元素:由于排序時無法比較null與其他元素,存入null會拋出NullPointerException
  • 排序效率穩(wěn)定:紅黑樹的增刪查時間復(fù)雜度均為O(log n),適合需要排序且數(shù)據(jù)量較大的場景。
2.1.4.4 HashSet 與 TreeSet 的對比選擇
對比維度HashSetTreeSet
底層結(jié)構(gòu)哈希表(數(shù)組 + 鏈表 / 紅黑樹)紅黑樹
排序特性完全無序有序(自然排序 / 自定義排序)
增刪查效率高(O (1),無哈希沖突時)中(O (log n))
去重依據(jù)hashCode() + equals()compareTo() / compare()返回 0
null 支持支持 1 個 null 元素不支持 null 元素
適用場景僅需去重,無需排序(如用戶 ID、標(biāo)簽)去重且需排序(如排序的商品價格、學(xué)號)

2.2 Map 接口:鍵值對存儲的雙列集合

Map是 Java 中專門用于存儲鍵值對(Key-Value) 的雙列集合,每個鍵(Key)對應(yīng)唯一的值(Value),鍵不可重復(fù),值可重復(fù),適用于 “通過鍵快速查找值” 的場景(如用戶信息表:Key 為用戶 ID,Value 為用戶對象)。

2.2.1 Map 的核心特性

  • 鍵值對結(jié)構(gòu):每個元素是一個 “鍵值對”(Java 中稱為Map.Entry對象),鍵與值一一對應(yīng),通過鍵可唯一確定值。
  • 鍵唯一:同一個Map中,鍵不能重復(fù)(重復(fù)添加會覆蓋原鍵對應(yīng)的值),值可以重復(fù)。
  • 無索引:不支持通過索引訪問元素,需通過鍵或鍵值對遍歷。
  • 引用類型存儲:鍵和值都必須是引用數(shù)據(jù)類型(基本類型需用包裝類,如int對應(yīng)Integer)。

2.2.2 Map 的通用方法

所有Map實現(xiàn)類(如HashMap、TreeMap)都支持以下通用方法:

// 創(chuàng)建Map對象(以HashMap為例)
Map<String, Integer> scoreMap = new HashMap<>();
// 1. 添加/修改鍵值對:鍵存在則覆蓋值,返回舊值;鍵不存在則添加,返回null
Integer oldScore = scoreMap.put("Alice", 95); // null(首次添加)
oldScore = scoreMap.put("Alice", 98); // 95(覆蓋舊值,返回舊值)
scoreMap.put("Bob", 88);
// 2. 刪除鍵值對:根據(jù)鍵刪除,返回被刪除的值;鍵不存在返回null
Integer removedScore = scoreMap.remove("Bob"); // 88
// 3. 判斷存在性
boolean hasAlice = scoreMap.containsKey("Alice"); // true(判斷鍵是否存在)
boolean has98 = scoreMap.containsValue(98); // true(判斷值是否存在)
// 4. 獲取值:根據(jù)鍵獲取值,鍵不存在返回null
Integer aliceScore = scoreMap.get("Alice"); // 98
// 5. 清空與長度
scoreMap.clear(); // 清空所有鍵值對
int size = scoreMap.size(); // 0(清空后長度為0)
boolean isEmpty = scoreMap.isEmpty(); // true

2.2.3 Map 的遍歷方式

Map的遍歷需圍繞 “鍵”“值”“鍵值對” 三種維度,共 3 種常用方式:

方式 1:遍歷鍵集(keySet ())—— 通過鍵找值

先獲取所有鍵的集合(keySet()),再遍歷鍵,通過get(Key)獲取對應(yīng)的值,適合僅需鍵和值的場景。

Map<String, Integer> scoreMap = new HashMap<>();
scoreMap.put("Alice", 95);
scoreMap.put("Bob", 88);
// 1. 獲取所有鍵的集合
Set<String> keys = scoreMap.keySet();
// 2. 遍歷鍵,獲取對應(yīng)值
for (String key : keys) {
    Integer value = scoreMap.get(key);
    System.out.println(key + " : " + value); // Alice:95, Bob:88
}
方式 2:遍歷鍵值對集(entrySet ())—— 直接遍歷鍵值對

獲取所有鍵值對的集合(entrySet()),每個元素是Map.Entry對象,可直接通過getKey()getValue()獲取鍵和值,效率比方式 1 高(無需多次調(diào)用get(Key))。

// 1. 獲取所有鍵值對的集合
Set<Map.Entry<String, Integer>> entrySet = scoreMap.entrySet();
// 2. 遍歷鍵值對
for (Map.Entry<String, Integer> entry : entrySet) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    System.out.println(key + " : " + value); // Alice:95, Bob:88
}
// Lambda簡化遍歷(Java 8+)
scoreMap.entrySet().forEach(entry -> {
    System.out.println(entry.getKey() + " : " + entry.getValue());
});
方式 3:遍歷值集(values ())—— 僅遍歷值

若僅需遍歷值,無需鍵,可通過values()獲取所有值的集合(Collection類型),直接遍歷。

// 1. 獲取所有值的集合
Collection<Integer> values = scoreMap.values();
// 2. 遍歷值
for (Integer value : values) {
    System.out.println(value); // 95, 88
}

2.2.4 HashMap:哈希表實現(xiàn)的高效鍵值對集合

HashMapMap最常用的實現(xiàn)類,底層基于哈希表(數(shù)組 + 鏈表 / 紅黑樹) 實現(xiàn),特點是無序、鍵唯一、支持 null 鍵和 null 值、增刪查效率高,適用于大多數(shù)鍵值對存儲場景。

(1)HashMap 的核心特性

  • 無序性:鍵值對的存儲順序與添加順序無關(guān),遍歷順序不固定。
  • null 支持:允許 1 個 null 鍵,允許多個 null 值(如map.put(null, 10); map.put(null, 20);會覆蓋為 null 鍵對應(yīng)的值 20)。
  • 線程不安全:多線程環(huán)境下,若同時修改HashMap(如添加 / 刪除元素),可能導(dǎo)致數(shù)據(jù)不一致或拋出ConcurrentModificationException,若需線程安全,可使用ConcurrentHashMap(推薦)或Collections.synchronizedMap(new HashMap<>())。

(2)HashMap 的擴(kuò)容機(jī)制

  • 初始容量:默認(rèn)初始容量為 16(數(shù)組長度),可通過構(gòu)造方法指定(如new HashMap<>(32))。
  • 負(fù)載因子:默認(rèn)值為 0.75,表示當(dāng)鍵值對數(shù)量超過 “容量 × 負(fù)載因子”(如 16×0.75=12)時,觸發(fā)擴(kuò)容。
  • 擴(kuò)容規(guī)則:擴(kuò)容時將數(shù)組長度擴(kuò)大為原容量的 2 倍(如 16→32→64...),并重新計算所有鍵的哈希值,將鍵值對遷移到新數(shù)組中(“重哈希”),頻繁擴(kuò)容會消耗性能,建議提前估算數(shù)據(jù)量,指定合適的初始容量。

(3)HashMap 的構(gòu)造方法

// 1. 默認(rèn)構(gòu)造:初始容量16,負(fù)載因子0.75
Map<String, Integer> map1 = new HashMap<>();
// 2. 指定初始容量:負(fù)載因子默認(rèn)0.75
Map<String, Integer> map2 = new HashMap<>(32);
// 3. 指定初始容量和負(fù)載因子
Map<String, Integer> map3 = new HashMap<>(32, 0.8f);
// 4. 從其他Map轉(zhuǎn)換
Map<String, Integer> tempMap = new HashMap<>();
tempMap.put("a", 1);
Map<String, Integer> map4 = new HashMap<>(tempMap);

2.2.5 TreeMap:紅黑樹實現(xiàn)的有序鍵值對集合

TreeMap底層基于紅黑樹實現(xiàn),特點是按鍵排序、鍵唯一、不支持 null 鍵、有序,適用于需要 “按鍵排序” 的鍵值對場景(如按日期排序的日志記錄:Key 為日期,Value 為日志內(nèi)容)。

(1)TreeMap 的排序方式

TreeSet類似,TreeMap的排序依賴鍵的比較邏輯,分為兩種方式:

自然排序:鍵的類需實現(xiàn)Comparable接口,并重寫compareTo()方法,按鍵的自然順序排序。

// 鍵為Integer(已實現(xiàn)Comparable),按鍵升序排序
Map<Integer, String> treeMap1 = new TreeMap<>();
treeMap1.put(3, "C");
treeMap1.put(1, "A");
treeMap1.put(2, "B");
// 遍歷輸出:1:A, 2:B, 3:C(按鍵升序)
for (Map.Entry<Integer, String> entry : treeMap1.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

自定義排序:創(chuàng)建TreeMap時傳入Comparator接口實現(xiàn)類,自定義鍵的排序規(guī)則。

// 鍵為String,按字符串長度降序排序
Map<String, Integer> treeMap2 = new TreeMap<>((k1, k2) -> {
    return k2.length() - k1.length(); // 按鍵長度降序
});
treeMap2.put("apple", 5);
treeMap2.put("banana", 6);
treeMap2.put("pear", 4);
// 遍歷輸出:banana:6, apple:5, pear:4(按鍵長度降序)
for (Map.Entry<String, Integer> entry : treeMap2.entrySet()) {
    System.out.println(entry.getKey() + ":" + entry.getValue());
}

(2)TreeMap 的特有方法(排序相關(guān))

由于TreeMap按鍵有序,提供了一些基于鍵排序的特有方法:

Map<Integer, String> treeMap = new TreeMap<>();
treeMap.put(1, "A");
treeMap.put(2, "B");
treeMap.put(3, "C");
treeMap.put(4, "D");
Integer firstKey = treeMap.firstKey(); // 獲取最小鍵(1)
Integer lastKey = treeMap.lastKey(); // 獲取最大鍵(4)
Integer lowerKey = treeMap.lowerKey(3); // 獲取小于3的最大鍵(2)
Integer higherKey = treeMap.higherKey(3); // 獲取大于3的最小鍵(4)
// 獲取鍵≥2且<4的子Map(包含2,不包含4)
Map<Integer, String> subMap = treeMap.subMap(2, true, 4, false);
// 子Map內(nèi)容:2:B, 3:C

2.2.6 HashMap 與 TreeMap 的對比選擇

對比維度HashMapTreeMap
底層結(jié)構(gòu)哈希表(數(shù)組 + 鏈表 / 紅黑樹)紅黑樹
排序特性無序(按哈希值存儲)有序(按鍵的自然 / 自定義順序)
增刪查效率高(O (1),無哈希沖突時)中(O (log n))
鍵的 null 支持允許 1 個 null 鍵不允許 null 鍵(會拋空指針)
線程安全不安全不安全
適用場景無需排序,需高效增刪查(如用戶信息、配置映射)需按鍵排序(如按日期的日志、按價格的商品映射)

三、不可變集合:線程安全的常量容器

不可變集合是創(chuàng)建后無法修改的集合(添加、刪除、修改元素會拋出UnsupportedOperationException),具有線程安全、不可篡改的特性,適用于存儲常量數(shù)據(jù)(如配置參數(shù)、固定枚舉值)或作為公共 API 的返回值(避免外部修改)。

3.1 不可變集合的創(chuàng)建方式

Java 提供 4 種創(chuàng)建不可變集合的方式,各有適用場景:

方式 1:Collections.unmodifiableXXX () —— 不可變視圖(淺拷貝)

通過Collections工具類的unmodifiableList()、unmodifiableSet()、unmodifiableMap()方法,從現(xiàn)有可變集合創(chuàng)建 “不可變視圖”。

  • 特點:視圖依賴原集合,原集合修改會同步影響視圖(淺拷貝),僅限制視圖的修改操作。
  • 示例
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UnmodifiableExample {
    public static void main(String[] args) {
        // 1. 創(chuàng)建可變集合
        List<String> mutableList = new ArrayList<>();
        mutableList.add("apple");
        // 2. 創(chuàng)建不可變視圖
        List<String> immutableList = Collections.unmodifiableList(mutableList);
        // 3. 視圖修改會拋異常
        // immutableList.add("banana"); // UnsupportedOperationException
        // 4. 原集合修改,視圖會同步變化(淺拷貝特性)
        mutableList.add("banana");
        System.out.println(immutableList); // 輸出:[apple, banana]
    }
}

方式 2:List/Set/Map.of () —— 直接創(chuàng)建不可變集合(深拷貝)

Java 9 + 中,List、Set、Map接口新增of()靜態(tài)方法,可直接傳入元素創(chuàng)建不可變集合,元素不可修改,且不依賴原集合(深拷貝)。

  • 特點
    • 元素不可重復(fù)(Set.of()、Map.of()),重復(fù)會拋IllegalArgumentException。
    • Map.of()最多支持 10 個鍵值對,超過需用Map.ofEntries()
  • 示例
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ImmutableOfExample {
    public static void main(String[] args) {
        // 1. 創(chuàng)建不可變List
        List<String> immutableList = List.of("apple", "banana", "cherry");
        // 2. 創(chuàng)建不可變Set(元素不可重復(fù))
        Set<Integer> immutableSet = Set.of(1, 2, 3);
        // 3. 創(chuàng)建不可變Map(鍵不可重復(fù),最多10個鍵值對)
        Map<String, Integer> immutableMap = Map.of(
            "apple", 1,
            "banana", 2,
            "cherry", 3
        );
        // 修改操作均拋異常
        // immutableList.add("date"); // UnsupportedOperationException
        // immutableSet.remove(1); // UnsupportedOperationException
        // immutableMap.put("pear", 4); // UnsupportedOperationException
    }
}

方式 3:Collectors.toUnmodifiableXXX () —— Stream 流收集(Java 10+)

Java 10 + 中,Collectors工具類新增toUnmodifiableList()、toUnmodifiableSet()toUnmodifiableMap()方法,可將Stream流的結(jié)果收集為不可變集合。

  • 示例
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ImmutableCollectorExample {
    public static void main(String[] args) {
        // 將Stream流中的字符串轉(zhuǎn)為大寫,收集為不可變List
        List<String> upperList = Stream.of("apple", "banana", "cherry")
            .map(String::toUpperCase)
            .collect(Collectors.toUnmodifiableList());
        // 修改拋異常
        // upperList.add("DATE"); // UnsupportedOperationException
        System.out.println(upperList); // 輸出:[APPLE, BANANA, CHERRY]
    }
}

方式 4:第三方庫(如 Guava)—— 更靈活的不可變集合

Google 的 Guava 庫提供了更強(qiáng)大的不可變集合實現(xiàn)(如ImmutableListImmutableMap),支持 Builder 模式,可創(chuàng)建任意長度的不可變集合,且性能優(yōu)于 JDK 原生方式。

  • 依賴(Maven)
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
public class GuavaImmutableExample {
    public static void main(String[] args) {
        // 1. 用of()創(chuàng)建不可變List
        ImmutableList<String> list = ImmutableList.of("a", "b", "c");
        // 2. 用Builder創(chuàng)建不可變Map(支持任意長度)
        ImmutableMap<String, Integer> map = ImmutableMap.<String, Integer>builder()
            .put("a", 1)
            .put("b", 2)
            .put("c", 3)
            .build();
        // 修改拋異常
        // list.add("d"); // UnsupportedOperationException
    }
}

3.2 不可變集合的核心優(yōu)勢與適用場景

  • 核心優(yōu)勢
    • 線程安全:無需同步鎖,多線程可安全共享,避免并發(fā)修改問題。
    • 不可篡改:數(shù)據(jù)創(chuàng)建后無法修改,保證數(shù)據(jù)一致性(如配置參數(shù)不被意外修改)。
    • 性能優(yōu)化:不可變集合無需預(yù)留擴(kuò)容空間,內(nèi)存占用更小,部分操作(如哈希值)可提前計算,提升效率。
  • 適用場景
    • 存儲固定不變的數(shù)據(jù)(如系統(tǒng)配置、枚舉列表、常量字典)。
    • 作為方法返回值(避免外部調(diào)用者修改集合內(nèi)容,保證 API 安全性)。
    • 多線程環(huán)境下共享數(shù)據(jù)(無需額外同步,簡化代碼)。

四、數(shù)組與集合的對比總結(jié)

對比維度數(shù)組集合
長度特性固定長度,創(chuàng)建后不可修改動態(tài)長度,支持自動擴(kuò)容
元素類型支持基本類型和引用類型僅支持引用類型(基本類型需用包裝類)
內(nèi)存存儲連續(xù)內(nèi)存空間非連續(xù)(如 LinkedList、HashSet)或部分連續(xù)(如 ArrayList)
核心方法無內(nèi)置方法,需手動實現(xiàn)(或用 Arrays 工具類)內(nèi)置增刪改查方法(add、remove、contains 等)
遍歷方式普通 for 循環(huán)、增強(qiáng) for 循環(huán)增強(qiáng) for 循環(huán)、迭代器、forEach(Lambda)
線程安全本身無線程安全特性,需手動同步大部分集合(ArrayList、HashMap)不安全,需用 Concurrent 系列或不可變集合
適用場景固定長度、頻繁查詢(如數(shù)組下標(biāo)訪問)動態(tài)長度、需頻繁增刪(如購物車、用戶列表)

五、知識擴(kuò)展

5.1 哈希表的工作原理(HashMap/HashSet 底層)

哈希表(Hash Table)是 “數(shù)組 + 鏈表 / 紅黑樹” 的組合結(jié)構(gòu),核心是通過哈希函數(shù)將鍵映射到數(shù)組的指定位置(桶),實現(xiàn)高效的增刪查:

  1. 哈希函數(shù):通過鍵的hashCode()計算哈希值,再通過 “哈希值 & (數(shù)組長度 - 1)”(等價于取模,效率更高)確定桶位置(數(shù)組索引)。
  2. 哈希沖突:不同鍵計算出相同桶位置的情況,解決方案:
    • 鏈表法:將同一桶中的元素連成鏈表,查詢時遍歷鏈表。
    • 紅黑樹法:JDK 8 + 中,當(dāng)鏈表長度超過 8 且數(shù)組長度≥64 時,鏈表轉(zhuǎn)為紅黑樹,將查詢時間復(fù)雜度從 O (n) 降至 O (log n)。
  3. 負(fù)載因子:控制哈希表的 “滿度”,默認(rèn) 0.75,平衡空間與時間效率:負(fù)載因子過高會增加哈希沖突,降低查詢效率;過低會浪費(fèi)內(nèi)存空間。

5.2 紅黑樹的特性(TreeMap/TreeSet 底層)

紅黑樹是一種自平衡的二叉搜索樹,通過以下規(guī)則保證平衡,確保增刪查時間復(fù)雜度為 O (log n):

  1. 每個節(jié)點要么是紅色,要么是黑色。
  2. 根節(jié)點是黑色。
  3. 所有葉子節(jié)點(NIL 節(jié)點)是黑色。
  4. 若一個節(jié)點是紅色,其兩個子節(jié)點必須是黑色(無連續(xù)紅色節(jié)點)。
  5. 從任意節(jié)點到其所有葉子節(jié)點的路徑中,黑色節(jié)點的數(shù)量相同(黑高一致)。
  • 當(dāng)插入或刪除節(jié)點破壞上述規(guī)則時,紅黑樹會通過 “旋轉(zhuǎn)”(左旋、右旋)和 “變色” 調(diào)整,恢復(fù)平衡狀態(tài)。

5.3 自動裝箱與拆箱(集合存儲基本類型的原理)

集合僅支持引用類型,存儲基本類型時需通過 “自動裝箱”(基本類型→包裝類)和 “自動拆箱”(包裝類→基本類型)實現(xiàn),本質(zhì)是編譯器的語法糖:

  • 自動裝箱:如list.add(10),編譯器自動轉(zhuǎn)為list.add(Integer.valueOf(10)),將int轉(zhuǎn)為Integer。
  • 自動拆箱:如int num = list.get(0),編譯器自動轉(zhuǎn)為int num = list.get(0).intValue(),將Integer轉(zhuǎn)為int。
  • 注意事項:避免在循環(huán)中頻繁裝箱(如for (int i=0; i<1000; i++) list.add(i)),會創(chuàng)建大量臨時Integer對象,建議提前手動裝箱或使用基本類型集合(如 Eclipse Collections、FastUtil)。

5.4 線程安全的集合類

默認(rèn)的集合類(如 ArrayList、HashMap、HashSet)均為線程不安全,多線程環(huán)境下需使用線程安全的集合,常用選擇:

  1. Concurrent 系列:JDK 提供的高效線程安全集合,如ConcurrentHashMap(HashMap 的線程安全版)、CopyOnWriteArrayList(ArrayList 的線程安全版),通過分段鎖、寫時復(fù)制等機(jī)制實現(xiàn)安全,性能優(yōu)于同步集合。
  2. 同步集合:通過Collections.synchronizedXXX()創(chuàng)建,如Collections.synchronizedList(new ArrayList<>()),底層用synchronized關(guān)鍵字加鎖,性能較低(全表鎖),適合并發(fā)量小的場景。
  3. 不可變集合:如List.of()、ImmutableList,本身不可修改,天然線程安全,適合存儲常量數(shù)據(jù)。

到此這篇關(guān)于JAVA基礎(chǔ)之?dāng)?shù)組和集合區(qū)別對比分析的文章就介紹到這了,更多相關(guān)java數(shù)組和集合內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java并發(fā)編程示例(十):線程組

    Java并發(fā)編程示例(十):線程組

    這篇文章主要介紹了Java并發(fā)編程示例(十):線程組,Java提供了ThreadGroup類來控制一個線程組,一個線程組可以通過線程對象來創(chuàng)建,也可以由其他線程組來創(chuàng)建,生成一個樹形結(jié)構(gòu)的線程,需要的朋友可以參考下
    2014-12-12
  • 詳解Java使用super和this來重載構(gòu)造方法

    詳解Java使用super和this來重載構(gòu)造方法

    這篇文章主要介紹了詳解Java使用super和this來重載構(gòu)造方法的相關(guān)資料,這里提供實例來幫助大家理解這部分內(nèi)容,需要的朋友可以參考下
    2017-08-08
  • java使用lambda表達(dá)式多條件排序方式

    java使用lambda表達(dá)式多條件排序方式

    這篇文章主要介紹了java使用lambda表達(dá)式多條件排序方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Spring Boot搭建文件上傳服務(wù)的方法

    Spring Boot搭建文件上傳服務(wù)的方法

    這篇文章主要為大家詳細(xì)介紹了Spring Boot搭建文件上傳服務(wù)的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-11-11
  • Java JDK 1.8 lambda的用法詳解

    Java JDK 1.8 lambda的用法詳解

    這篇文章主要介紹了Java JDK 1.8 lambda的用法詳解,文中給大家提到了jdk 1.8 Lambda 表達(dá)式 遍歷數(shù)組的方法,需要的朋友可以參考下
    2019-09-09
  • Java單例模式繼承覆蓋多態(tài)原理詳解

    Java單例模式繼承覆蓋多態(tài)原理詳解

    這篇文章主要介紹了Java單例模式繼承覆蓋多態(tài)原理詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-09-09
  • JAVA音頻處理依賴庫示例操作大全(從格式轉(zhuǎn)換到音頻拼接)

    JAVA音頻處理依賴庫示例操作大全(從格式轉(zhuǎn)換到音頻拼接)

    在現(xiàn)代應(yīng)用開發(fā)中,音頻處理是常見需求,包括格式轉(zhuǎn)換、音頻拼接、剪輯、降噪等操作,本教程將介紹Spring?Boot中常用的音頻處理依賴庫,比較它們的特性、區(qū)別及社區(qū)活躍程度,并提供實用代碼示例,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • java簡單列出文件夾下所有文件的方法

    java簡單列出文件夾下所有文件的方法

    這篇文章主要介紹了java簡單列出文件夾下所有文件的方法,涉及java針對文件夾遍歷操作相關(guān)技巧,需要的朋友可以參考下
    2016-08-08
  • 淺析JVM逃逸的原理及分析

    淺析JVM逃逸的原理及分析

    在本篇文章里我們給大家分享了JVM逃逸的原理及分析的相關(guān)知識點內(nèi)容,需要的讀者們可以學(xué)習(xí)下。
    2018-10-10
  • Springboot 項目一啟動就獲取HttpSession的兩種方法

    Springboot 項目一啟動就獲取HttpSession的兩種方法

    在SpringBoot項目中,HttpSession是有狀態(tài)的,通常只有在用戶發(fā)起 HTTP請求并建立會話后才會創(chuàng)建,因此,在項目啟動時是無法獲取到 HttpSession,下面就來介紹一下Springboot啟動就獲取HttpSession,感興趣的可以了解一下
    2025-10-10

最新評論

黔西| 岳普湖县| 喀什市| 广汉市| 宜宾市| 十堰市| 策勒县| 岗巴县| 英吉沙县| 云南省| 龙游县| 颍上县| 荥经县| 祥云县| 霍州市| 临海市| 汝南县| 南澳县| 潜山县| 宜川县| 永登县| 琼中| 黄龙县| 三穗县| 上饶市| 霍城县| 攀枝花市| 广南县| 安化县| 永川市| 岐山县| 玉林市| 宜州市| 四平市| 富川| 视频| 临泽县| 阿鲁科尔沁旗| 富民县| 河曲县| 巴马|