Java集合框架入门指南:从小白到基础掌握
一、初识集合框架
当我们处理多个数据元素时,数组的固定长度和单一类型限制就显得不够灵活。这时Java集合框架(Collection Framework)就像智能收纳盒应运而生,它提供了更强大的数据存储和操作能力。
集合框架的核心优势:
- 动态扩容:自动调整存储空间
- 丰富操作:内置搜索、排序等方法
- 类型安全:通过泛型保证数据一致性
- 高效算法:优化过的数据结构实现
常用集合的分类:
单列集合Collection
Collection集合的方法
package com.jjt.Collection;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;public class Collection02 {public static void main(String[] args) {//集合的常用方法System.out.println("集合的常用方法");Collection<String> list = new ArrayList<>();//添加元素list.add("hello");list.add("world");list.add("java");list.add("c++");System.out.println(list);//删除元素list.remove("hello");System.out.println(list);//判断集合是否为空System.out.println(list.isEmpty());//清空集合//list.clear();//判断集合是否包含某个元素System.out.println(list.contains("java"));//集合转换成数组Object[] arr = list.toArray();System.out.println(Arrays.toString(arr));//获取集合的大小System.out.println(list.size());//将一个集合中的元素给到另一个集合Collection<String> list2 = new ArrayList<>();}
}
Java 单列集合框架梳理
一、List 家族
特点:元素有序、可重复、支持索引访问
-
LinkedList
- 基于双向链表实现
- 插入/删除操作效率高($O(1)$)
- 随机访问效率较低($O(n)$)
- 非线程安全
-
ArrayList
- 基于动态数组实现
- 随机访问效率高($O(1)$)
- 增删操作可能触发数组扩容($O(n)$)
- 非线程安全
-
Vector
- 基于数组实现的同步集合
- 线程安全但性能较低
- 扩容策略与ArrayList不同
-
Stack
- 继承自Vector
- 实现后进先出(LIFO)栈结构
- 核心方法: $$push(E), pop(), peek()$$
二、Set 家族
特点:元素唯一、不重复
-
HashSet
- 基于哈希表实现(HashMap)
- 元素无序存储
- 插入/查询效率高($O(1)$)
- 允许存储null值
-
LinkedHashSet
- 继承HashSet
- 通过链表维护插入顺序
- 迭代顺序可预测
-
TreeSet
- 基于红黑树实现
- 元素自然排序或定制排序
- 插入/查询效率($O(\log n)$)
- 实现NavigableSet接口
关键特性对比
集合类型 | 数据结构 | 有序性 | 时间复杂度 | 线程安全 |
---|---|---|---|---|
ArrayList | 动态数组 | 插入序 | O(1)随机 | 否 |
LinkedList | 双向链表 | 插入序 | O(n)随机 | 否 |
HashSet | 哈希表 | 无序 | O(1) | 否 |
LinkedHashSet | 链表+哈希 | 插入序 | O(1) | 否 |
TreeSet | 红黑树 | 排序序 | O(log n) | 否 |
选择建议
- 需要快速随机访问 → ArrayList
- 频繁增删操作 → LinkedList
- 去重存储 → HashSet
- 保持插入顺序 → LinkedHashSet
- 自动排序需求 → TreeSet
Map 接口主要实现类对比及优化说明:
一、基础实现类
- Hashtable
- 特性:同步处理、线程安全
- 数据结构:数组+链表
- 注意:键值不允许为 null,建议用 ConcurrentHashMap 替代
- HashMap
- 特性:非同步、高效查询
- 数据结构:数组+链表/红黑树(JDK8+)
- 优化点:负载因子默认 0.75,扩容机制为 2 倍
二、有序实现类 3. LinkedHashMap
- 特性:维护插入/访问顺序
- 数据结构:哈希表+双向链表
- 应用场景:缓存实现、顺序访问需求
三、特殊实现类 4. WeakHashMap
- 特性:弱键引用机制
- 内存管理:自动回收无引用的键值对
- 应用场景:缓存临时映射
- TreeMap
- 特性:按键自然排序或自定义排序
- 数据结构:红黑树
- 排序方式:实现 Comparable 或提供 Comparator
- IdentityHashMap
- 特性:使用 == 代替 equals 比较键
- 哈希策略:System.identityHashCode()
- 应用场景:对象拓扑结构保持
四、选择建议
- 并发场景:优先考虑 ConcurrentHashMap
- 排序需求:根据排序类型选择 TreeMap 或 LinkedHashMap
- 特殊比较:对象标识符处理使用 IdentityHashMap
- 缓存场景:WeakHashMap 或 LinkedHashMap 配合移除策略
注:JDK8+ 中 HashMap 在哈希冲突时,链表长度超过 8 会自动转为红黑树结构(桶容量>64时),有效提升查询效率。
二、三大核心接口详解
1. List(有序集合)
特点:元素有序且可重复
// ArrayList示例
List<String> fruits = new ArrayList<>();
fruits.add("Apple"); // 索引0
fruits.add("Banana"); // 索引1
fruits.add(1, "Orange");// 在索引1插入
fruits.remove(int index)删除指定索引处的元素,返回被删除的元素
fruits.set(int index,e element) 修改指定索引处的元素,返回被修改的元素
fruits.get(int index)返回指定索引处的元素
System.out.println(fruits.get(2)); // 输出:Banana
实现类对比:功能差不多
ArrayList
:数组实现,随机访问快LinkedList
:链表实现,插入删除快 查询慢,无论查询那个数据都要从头开始找。
2. Set(唯一集合)
特点:元素唯一且无序,不重复,无索引
// HashSet示例
Set<Integer> numbers = new HashSet<>();
numbers.add(5);
numbers.add(5); // 重复元素不会被添加
System.out.println(numbers.size()); // 输出:1
常用实现:
HashSet
:哈希表存储,查询速度O(1)---LinkedHashSet有序,不重复,无索引TreeSet
:红黑树实现,自动排序,默认升序排列- Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
1.Vector是线程同步的,所以它也是线程安全的,而ArrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用ArrayList效率比较高。
2.如果集合中的元素的数目大于目前集合数组的长度时,在集合中使用数据量比较大的数据,用Vector有一定的优势。
3. 自定义对象的去重
默认情况下,HashSet
通过 equals()
和 hashCode()
判断对象是否相等。
若自定义类未重写这两个方法,HashSet
无法正确去重。
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}// 重写 equals 和 hashCode@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age && Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}
}// 使用 HashSet 去重
Set<Person> people = new HashSet<>();
people.add(new Person("Alice", 20));
people.add(new Person("Alice", 20)); // 重复对象,添加失败
. 去重应用场景
-
数据清洗:快速去除列表中的重复项。
-
唯一性校验:确保数据(如用户 ID、订单号)唯一。
-
集合操作:用于集合的交、并、差集运算。
TreeSet集合
注意事项:对于数值类型:Integer,Double,默认按照数值本身的大小进行升序排序
对于字符串类型:默认按照首字符的编号升序排序
对于自定义类型如Student对象,无法直接排序。需要重写比较方法
1.对象类型实现Comparable比较接口,重写compareTo方法,指定大小比较规则2.TreeSet(Comparator c)集合自带比较器Comparator对象,
方式二:构造时传入 Comparator
Set<Teacher> ts2 = new TreeSet<Teacher>(new Comparator<Teacher>() {@Overridepublic int compare(Teacher o1, Teacher o2) {return o1.getAge()-o2.getAge();}});
方式一:实现 Comparable 接口
package com.jjt.Collection;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@AllArgsConstructor
@NoArgsConstructor
@Data
public class Teacher implements Comparable<Teacher>{private String name;private int age;private String sex;@Override//重写打印方法public String toString() {return "Teacher{" +"name='" + name + '\'' +", age=" + age +", sex='" + sex + '\'' +'}'+"\n";}//t1.compareTo(t2)实际两个对象比较@Overridepublic int compareTo(Teacher o) {//this调t1,o调t2//java默认//如果左边大于右边返回正整数//如果左边小于右边返回负整数//如果左边等于右边返回0//根据年龄排序升序,降序颠倒对象位置return this.age-o.age;}
}
3. Queue(队列)
特点:先进先出(FIFO)的线性表
Queue<String> queue = new LinkedList<>();
queue.offer("First");
queue.offer("Second");
System.out.println(queue.poll()); // 输出:First
三、集合工具类实战
1. 遍历集合的三种方式
Collection<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);// 方法 1: 增强 for 循环
for (Integer num : numbers) {System.out.print(num + " "); // 10 20 30
}// 方法 2: 迭代器
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {System.out.print(iterator.next() + " ");
}// 方法 3: Java 8 forEach
numbers.forEach(num -> System.out.print(num + " "));// 3. 迭代器
Iterator<String> it = colors.iterator();
while(it.hasNext()){
//hasNext判断有没有下一个System.out.println(it.next());
//next移动到下一个
}
1. 增强 for 循环 (Enhanced For Loop)
-
只读安全:遍历时不能直接删除或修改元素,否则会抛出
ConcurrentModificationException
。 -
适用场景:快速遍历且无需修改集合内容。
2. 迭代器 (Iterator)
-
支持安全修改:可通过
iterator.remove()
安全删除当前元素(唯一允许在遍历中删除元素的方式)。 -
并发修改检测:如果在遍历过程中直接通过集合(如
collection.remove()
)修改集合,仍会抛出ConcurrentModificationException
。 -
适用场景:需要遍历时动态删除元素的场景(如过滤数据)。
2. 集合排序
List<Integer> nums = new ArrayList<>(Arrays.asList(3,1,4,2));// 自然排序
Collections.sort(nums); // [1,2,3,4]// 自定义排序
Collections.sort(nums, (a,b)->b-a); // [4,3,2,1]
四、实现类性能对比表
集合类型 | 底层结构 | 查询速度 | 增删速度 | 线程安全 | 有序性 |
---|---|---|---|---|---|
ArrayList | 数组 | O(1) | O(n) | 否 | 有序 |
LinkedList | 链表 | O(n) | O(1) | 否 | 有序 |
HashSet | 哈希表 | O(1) | O(1) | 否 | 无序 |
TreeSet | 红黑树 | O(log n) | O(log n) | 否 | 有序 |
五、最佳实践建议
- 快速查询选
ArrayList
,频繁增删用LinkedList
- 去重需求优先考虑
HashSet
- 需要排序时使用
TreeSet
- 多线程环境使用
Collections.synchronizedList()
包装 - 初始化时指定容量(如
new ArrayList<>(100)
)
双列集合Map
三、Map 详解
Map(映射)是 Java 集合框架中用于存储键值对(Key-Value)数据的顶层接口,与 Collection
接口并列,不继承 Collection
。其核心设计是通过唯一的键(Key)高效检索对应的值(Value),适用于需要精确键控访问的场景。
1. 核心特性
特性 | 说明 |
---|---|
键唯一性 | 每个 Key 在 Map 中唯一,重复添加会覆盖旧 Value。 |
Value 可重复 | 不同 Key 可映射到相同的 Value。 |
双列数据结构 | 数据以 Entry<Key, Value> 形式存储,支持通过 Key 直接定位 Value。 |
允许 Null 值 | 大多数实现类(如 HashMap )允许 Key 和 Value 为 null (具体取决于实现,如 Hashtable 禁止)。 |
2. Map vs Collection 数据结构对比
对比维度 | Map | Collection |
---|---|---|
数据单元 | 键值对(Key-Value) | 单元素(如 List 、Set ) |
存储方式 | 通过哈希表、树等结构维护 Key 映射 | 直接存储元素 |
核心操作 | put(key, value) 、get(key) | add(element) 、remove(element) |
唯一性约束 | Key 唯一 | Set 元素唯一,List 不唯一 |
3. Map 的三种集合视图
Map 提供三种视角访问其内容,均基于原 Map 动态更新:
-
Key 集合
-
方法:
Set<K> keySet()
-
特点:返回所有 Key 的
Set
视图(因 Key 唯一)。
Map<String, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); Set<String> keys = map.keySet(); // ["A", "B"]
-
-
Value 集合
-
方法:
Collection<V> values()
-
特点:返回所有 Value 的
Collection
视图(Value 可重复)。
Collection<Integer> values = map.values(); // [1, 2]
-
-
键值对集合
-
方法:
Set<Entry<K, V>> entrySet()
-
特点:返回所有
Entry
(键值对)的Set
视图,可直接操作 Key 和 Value。
for (Map.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + ": " + entry.getValue()); }
-
4. 常见实现类与选型建议
实现类 | 底层结构 | 特点 | 适用场景 |
---|---|---|---|
HashMap | 数组 + 链表/红黑树 | 无序,查询高效(O(1)),允许 Null Key/Value,线程不安全。 | 高频读写,无需排序。 |
LinkedHashMap | 链表 + 哈希表 | 保留插入顺序或访问顺序(LRU),性能略低于 HashMap 。 | 需要有序遍历的键值存储。 |
TreeMap | 红黑树 | Key 按自然顺序或自定义规则排序,操作时间复杂度 O(log n)。 | 需要有序 Key 或范围查询。 |
ConcurrentHashMap | 分段锁 + CAS | 线程安全,高并发优化,性能接近 HashMap 。 | 多线程环境下的高并发访问。 |
Hashtable | 哈希表 | 线程安全(全表锁),性能低,不允许 Null Key/Value,已逐渐被淘汰。 | 遗留系统兼容,不推荐新项目使用。 |
5. 关键注意事项
-
线程安全
-
HashMap
非线程安全,多线程环境下应使用ConcurrentHashMap
或Collections.synchronizedMap()
包装类。 -
示例:
Map<String, Integer> safeMap = Collections.synchronizedMap(new HashMap<>());
-
-
哈希一致性
-
若自定义对象作为 Key,必须正确重写
hashCode()
和equals()
方法,确保哈希计算和相等性判断准确。
-
-
遍历与修改
-
直接通过集合视图(如
keySet()
)遍历时删除元素会抛出ConcurrentModificationException
,应使用迭代器的remove()
方法。
Iterator<Map.Entry<String, Integer>> it = map.entrySet().iterator(); while (it.hasNext()) {Map.Entry<String, Integer> entry = it.next();if (entry.getKey().equals("A")) {it.remove(); // ✔️ 安全删除} }
-
-
性能优化
-
初始化
HashMap
时指定容量和负载因子,减少扩容次数。Map<String, Integer> map = new HashMap<>(16, 0.75f);
-
6. 常见方法速查
方法 | 作用 |
---|---|
V put(K key, V value) | 添加键值对,若 Key 存在则覆盖并返回旧 Value。 |
V get(Object key) | 根据 Key 获取 Value,未找到返回 null 。 |
boolean containsKey(Object key) | 判断是否包含指定 Key。 |
V remove(Object key) | 删除指定 Key 的键值对,返回被删除的 Value。 |
default V getOrDefault(K key, V defaultValue) | 安全获取 Value,未找到时返回默认值(Java 8+)。 |
default void forEach(BiConsumer<? super K, ? super V> action) | 遍历所有键值对(Java 8+)。 |
一、基础操作
1. 添加/更新键值对
-
V put(K key, V value)
-
作用:添加键值对。若 Key 已存在,则覆盖旧 Value,并返回旧 Value;若 Key 不存在,返回
null
。 -
示例:
Map<String, Integer> map = new HashMap<>(); map.put("A", 1); // 返回 null map.put("A", 100); // 返回 1,此时 Value 更新为 100
-
-
void putAll(Map<? extends K, ? extends V> m)
-
作用:将另一个 Map 的所有键值对批量添加到当前 Map。
-
示例:
Map<String, Integer> map1 = new HashMap<>(); map1.put("B", 2); map.putAll(map1); // map 变为 {"A":100, "B":2}
-
二、删除操作
-
V remove(Object key)
-
作用:删除指定 Key 的键值对,返回被删除的 Value;若 Key 不存在,返回
null
。 -
示例:
map.remove("B"); // 返回 2,map 变为 {"A":100}
-
-
default boolean remove(Object key, Object value)
-
作用(Java 8+):仅当 Key 和 Value 都匹配时删除键值对,返回是否成功。
-
示例:
map.remove("A", 1); // 返回 false(当前 Value 是 100) map.remove("A", 100); // 返回 true,map 变为空
-
-
void clear()
-
作用:清空所有键值对。
-
示例:
map.clear(); // map 变为 {}
-
三、查询操作
-
V get(Object key)
-
作用:根据 Key 获取 Value;若 Key 不存在,返回
null
。 -
示例:
map.get("A"); // 返回 100
-
-
boolean containsKey(Object key)
-
作用:判断 Map 是否包含指定 Key。
-
示例:
map.containsKey("A"); // 返回 true
-
-
boolean containsValue(Object value)
-
作用:判断 Map 是否包含指定 Value。
-
示例:
map.containsValue(100); // 返回 true
-
-
int size()
-
作用:返回键值对的数量。
-
示例:
map.size(); // 返回 1
-
-
boolean isEmpty()
-
作用:判断 Map 是否为空。
-
示例:
map.isEmpty(); // 返回 false
-
四、视图方法
-
Set<K> keySet()
-
作用:返回所有 Key 的
Set
视图(可遍历或删除元素)。 -
示例:
Set<String> keys = map.keySet(); // ["A"]
-
-
Collection<V> values()
-
作用:返回所有 Value 的
Collection
视图(可遍历或删除元素)。 -
示例:
Collection<Integer> values = map.values(); // [100]
-
-
Set<Map.Entry<K, V>> entrySet()
-
作用:返回所有键值对的
Set<Entry>
视图,支持遍历或修改 Value。 -
示例:
for (Map.Entry<String, Integer> entry : map.entrySet()) {entry.setValue(entry.getValue() + 10); // 修改 Value }
-
五、Java 8+ 新增方法
-
default V getOrDefault(Object key, V defaultValue)
-
作用:安全获取 Value,若 Key 不存在则返回默认值。
-
示例:
map.getOrDefault("C", 0); // 返回 0
-
-
default V putIfAbsent(K key, V value)
-
作用:仅当 Key 不存在时添加键值对,返回旧 Value 或
null
。 -
示例:
map.putIfAbsent("A", 200); // 返回 100(不覆盖) map.putIfAbsent("C", 3); // 返回 null,添加成功
-
-
default boolean replace(K key, V oldValue, V newValue)
-
作用:仅当 Key 和旧 Value 匹配时,更新为新 Value。
-
示例:
map.replace("A", 100, 200); // 返回 true
-
-
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
-
作用:根据 Key 和旧 Value 计算新 Value,若计算结果为
null
,则删除该 Key。 -
示例(将 Value 加倍):
map.compute("A", (k, v) -> v * 2); // Value 变为 200
-
-
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
-
作用:合并新旧 Value(常用于累加或去重)。
-
示例(合并字符串):
Map<String, String> map = new HashMap<>(); map.merge("A", "Hello", (oldVal, newVal) -> oldVal + newVal); // "Hello" map.merge("A", "World", (oldVal, newVal) -> oldVal + newVal); // "HelloWorld"
-
-
default void forEach(BiConsumer<? super K, ? super V> action)
-
作用:遍历所有键值对。
-
示例:
map.forEach((k, v) -> System.out.println(k + ": " + v));
-
六、批量操作
-
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
-
作用(Java 8+):对所有键值对应用函数替换 Value。
-
示例(所有 Value 加倍):
map.replaceAll((k, v) -> v * 2);
-
七、线程安全方法
-
ConcurrentHashMap
的原子操作(非Map
接口通用方法,但需注意):-
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
-
V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
-
总结
方法分类 | 核心方法 |
---|---|
增/改 | put() , putAll() , putIfAbsent() |
删 | remove() , clear() |
查 | get() , containsKey() , containsValue() , size() , isEmpty() |
视图 | keySet() , values() , entrySet() |
Java 8+ | getOrDefault() , compute() , merge() , forEach() , replaceAll() |
并发安全 | ConcurrentHashMap 的原子方法(需具体实现类支持) |
最佳实践
-
优先使用
getOrDefault
:避免null
导致的空指针异常。 -
遍历优化:使用
entrySet()
遍历而非多次调用get()
(减少哈希计算开销)。 -
线程安全:多线程环境下选择
ConcurrentHashMap
而非Collections.synchronizedMap()
。
总结
Map 是键值对存储的核心接口,通过唯一 Key 实现高效检索。根据场景选择合适的实现类(如高频读写用 HashMap
,有序需求用 TreeMap
),并注意线程安全、哈希一致性和遍历安全性问题。
总结
集合框架的学习就像搭建积木,理解各个组件的特点后,就能根据需求灵活组合。建议通过以下步骤巩固:
- 手写各集合的CRUD操作
- 比较不同实现类的性能差异
- 尝试实现自定义对象的排序
- 探索
Map
接口及其实现类
记得在实际编码中多使用Java Doc(Ctrl+Q
查看方法说明),逐步培养选择合适集合类型的能力。