Java 四大核心集合(List/Set/Queue/Map)详解

一、Java 集合框架总览

Java 集合框架(Java Collections Framework, JCF)是用于存储、操作数据的核心 API,位于 java.util 包下,核心目标是 提供统一、高效的数据结构操作接口,替代数组的局限性(固定长度、操作繁琐)。

1.1 核心体系结构

Java 集合框架分为两大核心分支:

  • Collection 接口:存储「单个元素」的集合,子接口包括 List(有序可重复)、Set(无序不可重复)、Queue(先进先出);
  • Map 接口:存储「键值对(Key-Value)」的集合,不继承 Collection,是独立的顶层接口。

1.2 核心接口关系表

顶层接口 核心子接口 / 实现类 存储类型 核心特征
Collection List(ArrayList、LinkedList) 单个元素 有序、可重复、有索引
Collection Set(HashSet、LinkedHashSet、TreeSet) 单个元素 无序、不可重复
Collection Queue(ArrayDeque、PriorityQueue、BlockingQueue) 单个元素 先进先出(FIFO)/ 优先级排序
Map HashMap、LinkedHashMap、TreeMap、ConcurrentHashMap 键值对 Key 唯一、Value 可重复

1.3 集合与数组的区别

特性 集合(如 List/Map) 数组
长度 动态扩容(无需指定初始长度) 固定长度(初始化后不可变)
存储类型 只能存储引用类型(基本类型需装箱为包装类) 可存储基本类型 / 引用类型
操作 API 提供丰富方法(add/remove/get/contains 等) 仅支持索引访问,无内置操作方法
灵活性 高(支持排序、遍历、查找、扩容) 低(需手动实现排序、扩容等逻辑)

二、List 详解:有序可重复的「动态数组」

2.1 核心特性

  • 有序性:元素存储顺序与插入顺序一致,支持通过「索引」访问(0 起始);
  • 可重复性:允许存储多个相等元素(equals() 返回 true);
  • 索引操作:支持通过索引增删改查(如 get(int index)remove(int index));
  • 非线程安全:默认实现(ArrayList、LinkedList)均为线程不安全,并发场景需手动同步。

2.2 常用实现类对比

实现类 底层结构 有序性 重复性 线程安全 查询效率 增删效率(中间 / 末尾) 初始容量 扩容机制
ArrayList 动态数组(Object []) 插入有序 允许 高(O (1),索引直接访问) 中间:低(O (n),需数组拷贝);末尾:高(O (1)) 10(JDK8) 扩容为原容量的 1.5 倍(oldCapacity + (oldCapacity >> 1)
LinkedList 双向链表 插入有序 允许 低(O (n),需遍历链表) 中间 / 末尾:高(O (1),仅需修改指针) 无(链表无容量概念) 无需扩容(链表节点动态新增)
Vector 动态数组(Object []) 插入有序 允许 是(方法加 synchronized 高(O (1)) 中间:低(O (n));末尾:高(O (1)) 10 扩容为原容量的 2 倍
CopyOnWriteArrayList CopyOnWrite 数组(写时复制) 插入有序 允许 是(读写分离) 高(O (1)) 低(O (n),写操作需复制整个数组) 0(初始为空数组) 扩容为原容量 + 1(每次写操作新建数组)

2.3 底层原理与关键机制

(1)ArrayList 核心机制
  • 动态扩容

    :当元素数量超过容量(size >= capacity)时,触发扩容:

    1. 计算新容量:newCapacity = oldCapacity + (oldCapacity >> 1)(1.5 倍);
    2. 若新容量仍不足(如手动添加大量元素),直接扩容为需求容量;
    3. 通过 Arrays.copyOf() 复制原数组到新数组,开销较大。
  • 缩容:默认不支持自动缩容(即使删除大量元素,容量仍不变),需手动调用 trimToSize() 释放空闲内存。

(2)LinkedList 核心机制
  • 底层是「双向链表」,每个节点(Node)包含 prev(前驱指针)、item(元素)、next(后继指针);
  • 实现了 Deque 接口,支持双端操作(如 addFirst()removeLast()),可作为队列 / 栈使用;
  • 无索引,查询需从表头 / 表尾遍历,效率低下。
(3)CopyOnWriteArrayList 核心机制
  • 「写时复制」:读操作直接访问原数组(无锁,高效),写操作(add/remove)时复制原数组生成新数组,修改后替换原数组引用;
  • 优点:读操作无并发冲突,无需加锁;缺点:写操作开销大,存在数据一致性问题(读可能获取旧数据)。

2.4 常用方法示例

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

public class ListDemo {
    public static void main(String[] args) {
        // 1. ArrayList 示例(查询优先)
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Java"); // 末尾添加:O(1)
        arrayList.add("Python");
        arrayList.add(1, "Golang"); // 中间插入:O(n),需数组拷贝
        System.out.println("ArrayList 元素:" + arrayList); // [Java, Golang, Python]
        System.out.println("索引 1 元素:" + arrayList.get(1)); // Golang(O(1))
        arrayList.remove(2); // 移除索引 2 元素:O(n)
        System.out.println("移除后:" + arrayList); // [Java, Golang]

        // 2. LinkedList 示例(增删优先,双端操作)
        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.addFirst("Redis"); // 表头添加:O(1)
        linkedList.addLast("MySQL"); // 表尾添加:O(1)
        linkedList.push("MongoDB"); // 栈操作(等价于 addFirst)
        System.out.println("LinkedList 元素:" + linkedList); // [MongoDB, Redis, MySQL]
        System.out.println("表头元素:" + linkedList.getFirst()); // MongoDB(O(1))
        System.out.println("表尾元素:" + linkedList.getLast()); // MySQL(O(1))
        linkedList.pop(); // 栈弹出(等价于 removeFirst)
        System.out.println("弹出后:" + linkedList); // [Redis, MySQL]
    }
}

2.5 线程安全问题与解决方案

(1)问题:

默认实现(ArrayList、LinkedList)在多线程并发读写时,可能出现 ConcurrentModificationException(快速失败)、数据错乱等问题。

(2)解决方案:
  1. 手动同步:使用 synchronizedReentrantLock 包裹集合操作;
  2. 使用线程安全实现类:
    • 低并发:Vector(效率低,不推荐);
    • 高并发读、低并发写:CopyOnWriteArrayList(读无锁,写复制);
  3. 工具类包装Collections.synchronizedList(new ArrayList<>())(底层通过同步代码块实现,效率一般)。

2.6 适用场景

  • ArrayList:查询频繁、增删少(如数据展示、配置列表);
  • LinkedList:增删频繁、查询少(如队列、栈、链表结构数据);
  • CopyOnWriteArrayList:高并发读场景(如缓存列表、日志收集);
  • Vector:遗留系统兼容(不推荐新代码使用)。

三、Set 详解:无序不可重复的「集合」

3.1 核心特性

  • 无序性:元素存储顺序与插入顺序无关(LinkedHashSet 除外,维护插入顺序);
  • 不可重复性:不允许存储相等元素(通过 equals()hashCode() 判断);
  • 无索引:不支持通过索引访问,仅支持遍历、包含判断;
  • 非线程安全:默认实现(HashSet、TreeSet)均为线程不安全。

3.2 常用实现类对比

实现类 底层结构 有序性 去重机制 线程安全 查找效率 插入效率 元素要求
HashSet 哈希表(基于 HashMap,元素存于 Key 位) 无序 hashCode() + equals() 高(O (1),哈希冲突时 O (n)) 高(O (1)) 需重写 hashCode()equals()
LinkedHashSet 哈希表 + 双向链表(基于 LinkedHashMap) 插入有序 hashCode() + equals() 高(O (1)) 中(比 HashSet 多维护链表) 需重写 hashCode()equals()
TreeSet 红黑树(基于 TreeMap,元素存于 Key 位) 自然排序 / 定制排序 Comparable.compareTo()Comparator 中(O (log n),红黑树查找) 中(O (log n)) 元素需可比较(实现 Comparable 或传入 Comparator)
CopyOnWriteArraySet CopyOnWrite 数组(基于 CopyOnWriteArrayList) 插入有序 equals() 是(读写分离) 低(O (n),需遍历数组) 低(O (n),写复制) 需重写 equals()

3.3 去重原理(核心!)

Set 的不可重复性依赖以下两个方法,必须同时满足:

  1. hashCode():计算元素的哈希值,决定元素在哈希表中的存储位置(桶位);
  2. equals():判断两个元素是否逻辑相等。
去重流程(以 HashSet 为例):
  1. 新增元素时,先调用元素的 hashCode() 得到哈希值,找到对应的桶位;
  2. 若桶位为空,直接存入元素;
  3. 若桶位不为空,遍历桶内元素,通过 equals()对比:
    • 若存在 equals() 返回 true 的元素,拒绝插入;
    • 若所有元素 equals() 返回 false,存入桶内(哈希冲突,链表 / 红黑树存储)。
关键规范:
  • a.equals(b) = true,则 a.hashCode() 必须等于 b.hashCode()
  • a.hashCode() = b.hashCode()a.equals(b) 可不为 true(哈希冲突);
  • 自定义对象作为 Set 元素时,必须重写 hashCode()equals(),否则无法保证去重。

3.4 常用方法示例

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;

public class SetDemo {
    static class User {
        private String id;
        private String name;

        public User(String id, String name) {
            this.id = id;
            this.name = name;
        }

        // 重写 equals() 和 hashCode()(关键:基于 id 去重)
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            User user = (User) o;
            return id.equals(user.id);
        }

        @Override
        public int hashCode() {
            return id.hashCode();
        }

        @Override
        public String toString() {
            return "User{id='" + id + "', name='" + name + "'}";
        }
    }

    public static void main(String[] args) {
        // 1. HashSet(无序去重)
        HashSet<User> hashSet = new HashSet<>();
        hashSet.add(new User("1", "张三"));
        hashSet.add(new User("2", "李四"));
        hashSet.add(new User("1", "张三(重复)")); // 去重(id 相同)
        System.out.println("HashSet 元素(无序):" + hashSet);
        // 输出:[User{id='1', name='张三'}, User{id='2', name='李四'}]

        // 2. LinkedHashSet(插入有序去重)
        LinkedHashSet<User> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add(new User("3", "王五"));
        linkedHashSet.add(new User("4", "赵六"));
        linkedHashSet.add(new User("3", "王五(重复)")); // 去重
        System.out.println("LinkedHashSet 元素(插入有序):" + linkedHashSet);
        // 输出:[User{id='3', name='王五'}, User{id='4', name='赵六'}]

        // 3. TreeSet(自然排序,需实现 Comparable)
        TreeSet<Integer> treeSet = new TreeSet<>();
        treeSet.add(3);
        treeSet.add(1);
        treeSet.add(2);
        treeSet.add(3); // 去重
        System.out.println("TreeSet 元素(自然排序):" + treeSet); // [1, 2, 3]

        // 定制排序(按 User.name 长度排序)
        TreeSet<User> customTreeSet = new TreeSet<>((u1, u2) -> u1.name.length() - u2.name.length());
        customTreeSet.add(new User("5", "小明"));
        customTreeSet.add(new User("6", "小红"));
        customTreeSet.add(new User("7", "王大明"));
        System.out.println("TreeSet 定制排序:" + customTreeSet);
        // 输出:[User{id='5', name='小明'}, User{id='6', name='小红'}, User{id='7', name='王大明'}]
    }
}

3.5 线程安全问题与解决方案

(1)问题:

默认实现(HashSet、TreeSet)并发读写时,可能出现 ConcurrentModificationException、数据重复插入等问题。

(2)解决方案:
  1. 工具类包装Collections.synchronizedSet(new HashSet<>())
  2. 并发安全实现类:
    • 高并发读、低并发写:CopyOnWriteArraySet(基于 CopyOnWriteArrayList,去重依赖 equals());
    • 高并发读写:ConcurrentSkipListSet(基于跳表,有序,线程安全,效率高于 CopyOnWriteArraySet)。

3.6 适用场景

  • HashSet:无需有序、需快速去重(如用户 ID 集合、标签集合);
  • LinkedHashSet:需有序去重(如历史记录、访问轨迹);
  • TreeSet:需排序的去重集合(如排行榜、有序数据筛选);
  • CopyOnWriteArraySet:高并发读场景的去重集合(如并发日志去重)。

四、Queue 详解:先进先出的「队列」

4.1 核心特性

  • FIFO 原则:默认先进先出(PriorityQueue 除外,按优先级排序);
  • 队列操作:支持「插入、移除、查看」三类核心操作,每种操作提供两种实现(失败抛异常 / 返回特殊值);
  • 双端队列:部分实现(如 LinkedList、ArrayDeque)支持双端操作(头 / 尾均可插入 / 移除),可作为栈使用;
  • 阻塞队列:支持阻塞等待(无元素时移除阻塞,无空间时插入阻塞),适用于并发场景。

4.2 核心方法对比(Queue 接口)

操作类型 失败抛异常 失败返回特殊值 说明
插入元素 add(E e) offer(E e) add 超出容量抛 IllegalStateException,offer 返回 false
移除元素 remove() poll() remove 无元素抛 NoSuchElementException,poll 返回 null
查看队首元素 element() peek() element 无元素抛 NoSuchElementException,peek 返回 null

4.3 常用实现类对比

实现类 底层结构 队列类型 有序性 线程安全 容量限制 核心特点
LinkedList 双向链表 双端队列(Deque) FIFO 无(动态扩容) 支持双端操作,可作为队列 / 栈使用
ArrayDeque 循环数组 双端队列(Deque) FIFO 无(动态扩容) 数组实现,效率高于 LinkedList,支持栈操作(push/pop)
PriorityQueue 小顶堆(数组实现) 优先级队列 自然排序 / 定制排序 无(动态扩容) 不遵循 FIFO,按优先级取出元素(默认小顶堆)
ConcurrentLinkedQueue 单向链表 并发队列 FIFO 是(CAS 无锁) 高并发场景下高效,非阻塞
ArrayBlockingQueue 数组 阻塞队列(BlockingQueue) FIFO 是(ReentrantLock) 有界(需指定容量) 数组实现,公平 / 非公平锁可选
LinkedBlockingQueue 双向链表 阻塞队列(BlockingQueue) FIFO 是(ReentrantLock) 可选有界(默认无界) 链表实现,吞吐量高于 ArrayBlockingQueue
SynchronousQueue 无存储结构 同步队列 FIFO 无界(但每次只能存 1 个元素) 插入操作需等待移除操作,适用于线程间数据传递

4.4 底层原理与关键机制

(1)PriorityQueue 核心机制
  • 底层是「小顶堆」(完全二叉树的数组实现),默认按自然排序(元素需实现 Comparable);
  • 插入元素时,通过「上浮」调整堆结构(确保父节点小于子节点);
  • 移除队首元素时,通过「下沉」调整堆结构(确保堆顶是最小值);
  • 注意:PriorityQueue 不是线程安全的,并发场景需使用 PriorityBlockingQueue
(2)BlockingQueue 核心机制
  • 基于「锁 + 条件变量」实现阻塞:
    • put(E e):队列满时阻塞,直到有空间;
    • take():队列空时阻塞,直到有元素;
    • 支持超时阻塞(offer(E e, long timeout, TimeUnit unit)poll(long timeout, TimeUnit unit));
  • 适用于生产者 - 消费者模型(生产者插入元素,消费者移除元素,自动协调并发)。

4.5 常用方法示例

import java.util.PriorityQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class QueueDemo {
    public static void main(String[] args) throws InterruptedException {
        // 1. PriorityQueue(优先级队列,小顶堆)
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(3);
        priorityQueue.offer(1);
        priorityQueue.offer(2);
        System.out.println("PriorityQueue 队首:" + priorityQueue.peek()); // 1(最小值)
        while (!priorityQueue.isEmpty()) {
            System.out.print(priorityQueue.poll() + " "); // 1 2 3(按优先级取出)
        }
        System.out.println();

        // 2. ArrayBlockingQueue(有界阻塞队列,生产者-消费者示例)
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(2); // 容量 2

        // 生产者线程
        new Thread(() -> {
            try {
                blockingQueue.put("任务1");
                System.out.println("生产者:放入任务1");
                blockingQueue.put("任务2");
                System.out.println("生产者:放入任务2");
                blockingQueue.put("任务3"); // 队列满,阻塞
                System.out.println("生产者:放入任务3");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "Producer").start();

        // 消费者线程
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1); // 休眠 1 秒,模拟处理时间
                System.out.println("消费者:取出" + blockingQueue.take()); // 取出任务1,队列有空间,生产者继续放入任务3
                TimeUnit.SECONDS.sleep(1);
                System.out.println("消费者:取出" + blockingQueue.take()); // 取出任务2
                TimeUnit.SECONDS.sleep(1);
                System.out.println("消费者:取出" + blockingQueue.take()); // 取出任务3
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "Consumer").start();
    }
}

4.6 适用场景

  • LinkedList/ArrayDeque:普通队列 / 栈操作(如任务临时存储、栈结构计算);
  • PriorityQueue:优先级任务调度(如任务优先级排序、Top K 问题);
  • ConcurrentLinkedQueue:高并发非阻塞队列(如日志收集、无阻塞任务队列);
  • ArrayBlockingQueue/LinkedBlockingQueue:生产者 - 消费者模型(如线程池任务队列、消息队列);
  • SynchronousQueue:线程间同步通信(如线程池的 SynchronousQueue 用于直接提交任务)。

五、Map 详解:键值对映射的「字典」

5.1 核心特性

  • 键值对存储:每个元素是 Entry<K, V> 对象(Key-Value 映射);
  • Key 唯一性:Key 不可重复(基于 hashCode()equals() 判断),Value 可重复;
  • 无序性:默认实现(HashMap)无序,LinkedHashMap 维护插入顺序,TreeMap 维护排序顺序;
  • 非线程安全:默认实现(HashMap、LinkedHashMap)均为线程不安全。

5.2 常用实现类对比

实现类 底层结构 有序性 Key 唯一性 线程安全 查找效率 插入效率 Key/Value 限制
HashMap JDK7:数组 + 链表;JDK8:数组 + 链表 / 红黑树 无序 hashCode() + equals() 高(O (1),冲突时 O (log n)) 高(O (1)) Key 可 null(仅 1 个),Value 可 null
LinkedHashMap 哈希表 + 双向链表 插入有序 / 访问有序 hashCode() + equals() 高(O (1)) 中(比 HashMap 多维护链表) Key 可 null,Value 可 null
TreeMap 红黑树 自然排序 / 定制排序 Comparable.compareTo()Comparator 中(O (log n)) 中(O (log n)) Key 不可 null,Value 可 null
Hashtable 数组 + 链表 无序 hashCode() + equals() 是(方法加 synchronized 低(O (1),冲突时 O (n)) Key/Value 均不可 null
ConcurrentHashMap JDK7:分段锁;JDK8:数组 + 链表 / 红黑树 + CAS+synchronized 无序 hashCode() + equals() 是(并发安全) 高(接近 HashMap) 高(接近 HashMap) Key/Value 均不可 null

5.3 底层原理与关键机制

(1)HashMap 核心机制(JDK8)
  • 底层结构:数组(桶)+ 链表 + 红黑树(哈希冲突解决);
    • 数组:存储 Entry 对象,初始容量 16(2^4),必须是 2 的幂(便于哈希计算);
    • 链表:哈希冲突时,元素链化(链表长度 <= 8 时);
    • 红黑树:链表长度 >= 8 且数组长度 >= 64 时,链表转为红黑树(提升查询效率)。
  • 哈希计算index = (n - 1) & hash(n 为数组长度,保证 index 在数组范围内);
  • 负载因子:默认 0.75(平衡空间与时间),当 size > capacity * loadFactor 时触发扩容;
  • 扩容机制:扩容为原容量的 2 倍,重新计算所有元素的哈希值并迁移(rehash)。
(2)ConcurrentHashMap 核心机制(JDK8)
  • 放弃 JDK7 的「分段锁」,采用「CAS + synchronized」实现并发安全:
    • 对数组桶的头节点加 synchronized 锁,减少锁竞争(粒度更细);
    • 读操作无锁(volatile 保证可见性);
    • 支持高并发读写,吞吐量远高于 Hashtable。
(3)LinkedHashMap 核心机制
  • 基于 HashMap,额外维护一条「双向链表」,记录元素的插入顺序或访问顺序;
  • 可通过构造函数指定访问顺序(accessOrder = true),适用于 LRU 缓存(最近最少使用淘汰)。

5.4 常用方法示例

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

public class MapDemo {
    public static void main(String[] args) {
        // 1. HashMap(无序,Key 唯一)
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("Java", 95);
        hashMap.put("Python", 85);
        hashMap.put("Java", 100); // Key 重复,覆盖 Value
        System.out.println("HashMap:" + hashMap); // {Java=100, Python=85}(无序)
        System.out.println("获取 Java 分数:" + hashMap.get("Java")); // 100
        System.out.println("是否包含 Key Python:" + hashMap.containsKey("Python")); // true

        // 2. LinkedHashMap(插入有序)
        Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put("Java", 95);
        linkedHashMap.put("Python", 85);
        linkedHashMap.put("Golang", 90);
        System.out.println("LinkedHashMap(插入有序):" + linkedHashMap);
        // 输出:{Java=95, Python=85, Golang=90}

        // 3. TreeMap(自然排序)
        Map<String, Integer> treeMap = new TreeMap<>();
        treeMap.put("C", 80);
        treeMap.put("A", 90);
        treeMap.put("B", 85);
        System.out.println("TreeMap(自然排序):" + treeMap); // {A=90, B=85, C=80}

        // 4. ConcurrentHashMap(并发安全)
        Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
        // 多线程并发操作(无异常)
        new Thread(() -> concurrentHashMap.put("线程1", 1)).start();
        new Thread(() -> concurrentHashMap.put("线程2", 2)).start();
        new Thread(() -> System.out.println("ConcurrentHashMap:" + concurrentHashMap)).start();
    }
}

5.5 线程安全问题与解决方案

(1)问题:

默认实现(HashMap、LinkedHashMap)并发读写时,可能出现 ConcurrentModificationException、数据错乱、扩容死循环(JDK7)等问题。

(2)解决方案:
  1. 工具类包装Collections.synchronizedMap(new HashMap<>())(底层同步代码块,效率一般);
  2. 线程安全实现类:
    • 遗留系统:Hashtable(效率低,不推荐);
    • 高并发场景:ConcurrentHashMap(JDK8 效率高,推荐);
    • 有序并发:ConcurrentSkipListMap(基于跳表,有序,并发安全)。

5.6 适用场景

  • HashMap:普通键值对映射(如配置存储、缓存、字典查询);
  • LinkedHashMap:有序键值对(如历史记录、LRU 缓存);
  • TreeMap:有序键值对排序(如排行榜、区间查询);
  • ConcurrentHashMap:高并发键值对映射(如分布式缓存、并发配置存储);
  • ConcurrentSkipListMap:高并发有序键值对(如并发排行榜)。

六、四大集合核心对比总结

集合类型 核心特征 有序性 重复性 线程安全(默认) 常用实现类 核心用途
List 索引访问 插入有序 允许 ArrayList、LinkedList 动态数组、有序列表
Set 不可重复 无序(LinkedHashSet 除外) 禁止 HashSet、TreeSet 去重、集合运算
Queue FIFO / 优先级 FIFO(PriorityQueue 除外) 允许 ArrayDeque、BlockingQueue 队列、任务调度
Map 键值对 无序(LinkedHashSet/TreeMap 除外) Key 禁止、Value 允许 HashMap、ConcurrentHashMap 字典映射、缓存

七、常见问题与注意事项

7.1 equals () 与 hashCode () 重写规范

  • 适用场景:Set 元素、Map Key 为自定义对象时;
  • 规范:
    1. a.equals(b) = true,则 a.hashCode() = b.hashCode()
    2. a.hashCode() != b.hashCode(),则 a.equals(b) = false
    3. 重写 equals() 必须重写 hashCode(),反之不必然;
  • 示例:基于对象的核心字段(如 ID)重写,避免使用默认的 Object 类实现(基于内存地址)。

7.2 fail-fast 与 fail-safe 机制

  • fail-fast(快速失败)
    • 触发场景:ArrayList、HashMap 等集合在迭代时,若其他线程修改集合(add/remove),会抛出 ConcurrentModificationException
    • 原理:迭代器维护 modCount(修改次数),迭代时检查 modCount 是否变化;
    • 注意:单线程迭代时,若通过集合自身方法(而非迭代器)修改集合,也会触发。
  • fail-safe(安全失败)
    • 触发场景:CopyOnWriteArrayList、ConcurrentHashMap 等并发集合;
    • 原理:迭代时访问集合的「快照」(如 CopyOnWrite 数组的原数组),修改操作不影响迭代;
    • 缺点:可能读取到旧数据,写操作开销大。

7.3 集合选型建议

  1. 需有序、可重复、索引访问List
    • 查询多 → ArrayList;
    • 增删多 → LinkedList;
    • 并发读 → CopyOnWriteArrayList。
  2. 需去重Set
    • 无序 → HashSet;
    • 有序 → LinkedHashSet;
    • 排序 → TreeSet;
    • 并发 → CopyOnWriteArraySet。
  3. 需队列 / 栈操作Queue
    • 普通队列 / 栈 → ArrayDeque;
    • 优先级 → PriorityQueue;
    • 并发阻塞 → ArrayBlockingQueue/LinkedBlockingQueue。
  4. 需键值对映射Map
    • 普通映射 → HashMap;
    • 有序映射 → LinkedHashMap/TreeMap;
    • 并发映射 → ConcurrentHashMap。

八、总结

Java 四大核心集合(List/Set/Queue/Map)是日常开发中最常用的数据结构,其设计遵循「接口统一、实现差异化」的原则:

  • List 侧重「有序可重复」,提供索引访问;
  • Set 侧重「无序不可重复」,提供去重能力;
  • Queue 侧重「队列操作」,支持 FIFO / 优先级 / 阻塞;
  • Map 侧重「键值对映射」,提供高效查找。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐