JAVA基礎(chǔ)之?dāng)?shù)組和集合區(qū)別對比分析
一、數(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。
- 基本數(shù)據(jù)類型數(shù)組:存儲具體數(shù)值(如
- 連續(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ù))返回false | List<String> list = new ArrayList<>(); list.add("Java"); |
boolean remove(Object o) | 刪除集合中指定元素,成功返回true,無此元素返回false | list.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) | 判斷集合是否包含指定元素,包含返回true | boolean hasJava = list.contains("Java"); |
boolean isEmpty() | 判斷集合是否為空(長度 0),空返回true | boolean 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ù)的單列集合
List是Collection的子接口,特點是有序(存儲與取出順序一致)、可重復(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)為 ArrayList | Set<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 的對比選擇
| 對比維度 | ArrayList | LinkedList |
|---|---|---|
| 底層結(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ù)的單列集合
Set是Collection的子接口,特點是無序(存儲與取出順序可能不一致)、不可重復(fù)(元素值唯一)、無索引,適用于需要 “去重” 的場景(如用戶 ID 列表、不重復(fù)的標(biāo)簽)。
2.1.4.1 Set 的核心特性(補(bǔ)充 Collection)
- 元素不可重復(fù):添加重復(fù)元素時,
add()方法返回false,集合不會存儲重復(fù)值(去重邏輯由具體實現(xiàn)類決定)。 - 無索引:不支持通過索引訪問元素,因此無法使用普通
for循環(huán)遍歷,只能用迭代器、增強(qiáng)for或forEach。 - 無序性:
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()方法,具體流程如下:
- 調(diào)用新增元素的
hashCode()方法,計算其哈希值,根據(jù)哈希值確定在哈希表中的 “桶位置”(數(shù)組索引)。 - 若該桶位置為空,直接將元素存入(無重復(fù))。
- 若該桶位置不為空(哈希沖突),則調(diào)用元素的
equals()方法,與桶中已有的元素逐一比較:- 若
equals()返回true:元素重復(fù),不存入。 - 若
equals()返回false:元素不重復(fù),將元素存入桶中(JDK 8 + 中,若桶中元素超過 8 個,鏈表會轉(zhuǎn)為紅黑樹,提升查詢效率)。
- 若
因此,為了確保 HashSet 能夠正確判斷元素的唯一性,需要重寫元素類的 hashCode() 和 equals() 方法。
(2)關(guān)鍵注意事項
- 若自定義類(如
Student、Book)的對象要存入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(); // 12.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 的對比選擇
| 對比維度 | HashSet | TreeSet |
|---|---|---|
| 底層結(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(); // true2.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)的高效鍵值對集合
HashMap是Map最常用的實現(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 的對比選擇
| 對比維度 | HashMap | TreeMap |
|---|---|---|
| 底層結(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()。
- 元素不可重復(fù)(
- 示例:
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)(如ImmutableList、ImmutableMap),支持 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)高效的增刪查:
- 哈希函數(shù):通過鍵的
hashCode()計算哈希值,再通過 “哈希值 & (數(shù)組長度 - 1)”(等價于取模,效率更高)確定桶位置(數(shù)組索引)。 - 哈希沖突:不同鍵計算出相同桶位置的情況,解決方案:
- 鏈表法:將同一桶中的元素連成鏈表,查詢時遍歷鏈表。
- 紅黑樹法:JDK 8 + 中,當(dāng)鏈表長度超過 8 且數(shù)組長度≥64 時,鏈表轉(zhuǎn)為紅黑樹,將查詢時間復(fù)雜度從 O (n) 降至 O (log n)。
- 負(fù)載因子:控制哈希表的 “滿度”,默認(rèn) 0.75,平衡空間與時間效率:負(fù)載因子過高會增加哈希沖突,降低查詢效率;過低會浪費(fèi)內(nèi)存空間。
5.2 紅黑樹的特性(TreeMap/TreeSet 底層)
紅黑樹是一種自平衡的二叉搜索樹,通過以下規(guī)則保證平衡,確保增刪查時間復(fù)雜度為 O (log n):
- 每個節(jié)點要么是紅色,要么是黑色。
- 根節(jié)點是黑色。
- 所有葉子節(jié)點(NIL 節(jié)點)是黑色。
- 若一個節(jié)點是紅色,其兩個子節(jié)點必須是黑色(無連續(xù)紅色節(jié)點)。
- 從任意節(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)境下需使用線程安全的集合,常用選擇:
- Concurrent 系列:JDK 提供的高效線程安全集合,如
ConcurrentHashMap(HashMap 的線程安全版)、CopyOnWriteArrayList(ArrayList 的線程安全版),通過分段鎖、寫時復(fù)制等機(jī)制實現(xiàn)安全,性能優(yōu)于同步集合。 - 同步集合:通過
Collections.synchronizedXXX()創(chuàng)建,如Collections.synchronizedList(new ArrayList<>()),底層用synchronized關(guān)鍵字加鎖,性能較低(全表鎖),適合并發(fā)量小的場景。 - 不可變集合:如
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使用super和this來重載構(gòu)造方法
這篇文章主要介紹了詳解Java使用super和this來重載構(gòu)造方法的相關(guān)資料,這里提供實例來幫助大家理解這部分內(nèi)容,需要的朋友可以參考下2017-08-08
JAVA音頻處理依賴庫示例操作大全(從格式轉(zhuǎn)換到音頻拼接)
在現(xiàn)代應(yīng)用開發(fā)中,音頻處理是常見需求,包括格式轉(zhuǎn)換、音頻拼接、剪輯、降噪等操作,本教程將介紹Spring?Boot中常用的音頻處理依賴庫,比較它們的特性、區(qū)別及社區(qū)活躍程度,并提供實用代碼示例,感興趣的朋友跟隨小編一起看看吧2025-10-10
Springboot 項目一啟動就獲取HttpSession的兩種方法
在SpringBoot項目中,HttpSession是有狀態(tài)的,通常只有在用戶發(fā)起 HTTP請求并建立會話后才會創(chuàng)建,因此,在項目啟動時是無法獲取到 HttpSession,下面就來介紹一下Springboot啟動就獲取HttpSession,感興趣的可以了解一下2025-10-10

