13_集合

# 集合

集合是 Java 中用于存储、操作多个对象的容器,相比数组,集合的长度可变、支持丰富的操作方法、可以存储不同类型的对象(泛型约束后可统一类型)。

image-20260727141415149

Java集合框架位于java.util包中

Collection是Set和List的父类,Collections是工具类,提供了对集合进行排序、遍历等多种算法的实现。

ArrayList: 有序(放进去顺序和拿出来顺序一致),可重复

HashSet: 无序(放进去顺序和拿出来顺序不一定一致),不可重复

接口可以继承接口

Java 集合框架主要分为两大分支:

  1. Collection 接口

:单列集合,存储一个个独立的元素

  • List:有序、可重复
  • Set:无序、不可重复
  1. Map 接口:双列集合,存储键值对(Key-Value),Key 不可重复,Value 可重复

Collection 接口

Collection 是所有单列集合的父接口,定义了集合通用的方法。

Collection 通用方法

表格

方法 作用
boolean add(E e) 向集合中添加一个元素
boolean remove(Object o) 删除集合中指定的元素
boolean contains(Object o) 判断集合是否包含指定元素
int size() 返回集合中元素的个数
boolean isEmpty() 判断集合是否为空
void clear() 清空集合中所有元素
Object[] toArray() 将集合转为数组
Iterator<E> iterator() 获取集合的迭代器,用于遍历

Collection 是 Java 单列集合的根接口,核心分为三大子体系:List(有序可重复)、Set(无序不可重复)、Queue(队列 / 栈结构)。下面按子接口分类,从底层结构、性能、线程安全、适用场景等维度完整对比所有常用实现类。

List 接口实现类对比

List 核心特征:存取顺序一致、支持索引访问、允许元素重复,是开发中最常用的集合类型。

表格

实现类 底层数据结构 有序性 元素重复 线程安全 查询性能 首尾增删性能 中间增删性能 默认初始容量 扩容机制 核心特点
ArrayList Object 动态数组 插入有序,支持索引 允许 不安全 极快(O (1)) 一般(可能触发扩容) 慢(需移动元素 O (n)) JDK8+ 默认为 0,首次 add 初始化为 10 每次扩容为原容量的 1.5 倍 最常用,查询为主的场景首选
LinkedList 双向链表(Node 节点) 插入有序,支持索引 允许 不安全 慢(O (n)) 极快(O (1)) 慢(需遍历定位 O (n)) 无初始容量,节点按需创建 无扩容机制,链表无限追加 同时实现了 Deque 接口,可做队列 / 栈
Vector Object 动态数组 插入有序,支持索引 允许 安全(方法全加 synchronized) 较快(加锁有开销) 慢(加锁 + 扩容) 慢 默认初始容量 10 每次扩容为原容量的 2 倍 古老实现类,性能差,已基本淘汰
Stack 继承 Vector,数组实现 插入有序 允许 安全 一般 栈顶操作快 慢 同 Vector 同 Vector 栈结构(后进先出),官方已不推荐,优先用 ArrayDeque

关键补充说明

  1. ArrayList 扩容细节 扩容时会创建新数组并将原数组元素拷贝过去,频繁扩容会影响性能;如果预知元素数量,建议通过 new ArrayList<>(initialCapacity) 指定初始容量,减少扩容次数。
  2. LinkedList 的双重身份 它同时实现了 List 和 Deque 接口,既可以当普通列表,也可以作为队列(Queue)或双端队列 / 栈使用。
  3. 为什么不推荐 Vector Vector 的线程安全是通过给所有方法加重量级锁实现的,并发性能极低;现代多线程场景更推荐用 CopyOnWriteArrayList 替代。

Set 接口实现类对比

Set 核心特征:元素不可重复、大部分实现存取无序,主要用于去重、元素判重场景。

表格

实现类 底层数据结构 有序性 去重依据 线程安全 增删查性能 支持 null 元素 排序能力 适用场景
HashSet HashMap(哈希表:数组 + 链表 / 红黑树) 无序(存取顺序不一致) hashCode() + equals() 不安全 快(O (1)) 允许 1 个 null 无 通用去重、判重,不要求顺序的场景
LinkedHashSet LinkedHashMap(哈希表 + 双向链表) 插入有序(记录添加顺序) hashCode() + equals() 不安全 略慢于 HashSet(维护链表开销) 允许 1 个 null 无 需要去重且保证插入顺序的场景
TreeSet TreeMap(红黑树) 排序有序(按元素大小排序) compareTo() / compare() 返回 0 不安全 较快(O (logn)) 不允许(null 无法比较) 支持自然排序 / 定制排序 需要去重且对元素自动排序的场景
EnumSet 位向量(long 数组) 按枚举定义顺序排列 枚举类型天然唯一 不安全 极快(位运算) 不允许 按枚举常量排序 专门存储枚举类型元素,性能最高

关键补充说明

  1. HashSet 去重原理
  • 调用元素的 hashCode() 计算哈希值,确定在数组中的存储位置
  • 若该位置无元素,直接存入
  • 若该位置已有元素,调用
     equals()
     

逐一比较内容

  • 内容相同:判定为重复元素,添加失败
  • 内容不同:以链表 / 红黑树形式挂载在该位置

自定义类要实现去重,必须同时重写 hashCode() 和 equals() 方法。

  1. TreeSet 的排序前提

TreeSet 中的元素必须支持比较,两种实现方式:

  • 元素类实现 Comparable 接口(自然排序)
  • 创建 TreeSet 时传入 Comparator 比较器(定制排序)
  1. LinkedHashSet 的有序本质 它在 HashSet 的哈希结构基础上,额外维护了一条双向链表记录元素的插入顺序,因此遍历顺序等于添加顺序。

Queue / Deque 接口实现类对比

Queue(队列)是先进先出(FIFO)的集合;Deque(双端队列)是 Queue 的子接口,两端都可插入删除,既能当队列也能当栈。

表格

实现类 底层数据结构 线程安全 有序性 核心特性 适用场景
ArrayDeque 循环数组 不安全 插入有序 双端操作 O (1),无容量上限,自动扩容 推荐作为栈 / 队列使用,性能远超 Stack 和 LinkedList
LinkedList 双向链表 不安全 插入有序 双端操作 O (1),同时是 List 实现 需要同时兼顾列表和队列功能的场景
PriorityQueue 二叉堆(默认小顶堆) 不安全 按优先级排序 元素按优先级出队,而非插入顺序 优先级任务、TOP K 等需要按权重排序的场景

关键补充说明

  • ArrayDeque 是栈和队列的首选:作为栈比古老的 Stack 快,作为队列比 LinkedList 快(数组的缓存命中率更高)。
  • PriorityQueue 注意点:元素必须支持比较(同 TreeSet),默认是自然升序(小顶堆),可通过 Comparator 改为大顶堆;不支持存储 null。

并发安全的 Collection 实现(java.util.concurrent)

上述所有实现类(除 Vector/Stack)都不是线程安全的,多线程场景推荐使用并发包下的专用实现:

表格

接口 并发实现类 线程安全机制 特点
List CopyOnWriteArrayList 写时复制(Copy-On-Write) 读无锁,写时复制新数组,读多写少场景性能好
Set CopyOnWriteArraySet 基于 CopyOnWriteArrayList 实现 写时复制,适合读多写少的并发去重场景
Set ConcurrentSkipListSet 跳表结构 + CAS 并发有序集合,替代 TreeSet 的并发版本
Queue ArrayBlockingQueue 数组 + 重入锁 有界阻塞队列,常用于生产者消费者模型
Queue LinkedBlockingQueue 链表 + 双锁 可设置容量的阻塞队列,吞吐量高于 ArrayBlockingQueue

List 接口(有序、可重复)

List 集合的元素存取有序、支持索引、允许重复,常用实现类有 ArrayList、LinkedList、Vector。

List 特有方法(带索引操作)

表格

方法 作用
void add(int index, E element) 在指定索引位置插入元素
E get(int index) 获取指定索引位置的元素
E remove(int index) 删除指定索引位置的元素
E set(int index, E element) 修改指定索引位置的元素
int indexOf(Object o) 查找元素第一次出现的索引
@Test
public void test1() {
    //数组最大问题是长度固定,而且要操作下标
    Student[] array = new Student[3];

    ArrayList<Student> list = new ArrayList<>();
    Student student1 = new Student();
    Student student2 = new Student();
    Student student3 = new Student();
    Student student4 = new Student();
    list.add(student1);
    list.add(student2);
    list.add(student3);
    list.add(student4);
    list.add(student1);

    //有序可重复
    //有序:你放进去的顺序和拿出来的顺序一致
    //ArrayList<String> list1 = new ArrayList<>();
    List<String> list1 = new ArrayList<>();
    list1.add("Java");
    list1.add("UI");
    list1.add("H5");
    list1.add("H5");
    list1.add("aa");
    for (String str : list1) {
        System.out.println(str);
    }
    System.out.println("-------------------");
    //无序不重复
    //无序:放进去顺序和拿出来的顺序可能是不一致的
    //HashSet<String> set = new HashSet<String>();
    Set<String> set = new HashSet<>();
    set.add("Java");
    set.add("UI");
    set.add("H5");
    set.add("H5");
    set.add("aa");
    for (String str : set) {
        System.out.println(str);
    }
}

1.8版本以及之后的版本泛型后面的数据类型可以不用写

ArrayList和LinkedList区别

ArrayList和LinkedList的大致区别如下:

1. ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2. 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3. 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

实现类 底层结构 特点 线程安全 适用场景
ArrayList 动态数组 查询快、增删慢 不安全 频繁查询、少量增删
LinkedList 双向链表 查询慢、首尾增删快 不安全 频繁首尾增删、队列 / 栈场景
Vector 动态数组 查询快、增删慢 安全(方法加锁) 多线程场景(已不推荐)

Map

Map 用于存储键值对(Key-Value),Key 唯一不可重复,Value 可重复。

Map 常用方法

方法 作用
V put(K key, V value) 添加键值对;key 已存在则覆盖 value
V get(Object key) 根据 key 获取对应的 value
V remove(Object key) 根据 key 删除键值对
boolean containsKey(Object key) 判断是否包含指定的 key
boolean containsValue(Object value) 判断是否包含指定的 value
int size() 返回键值对的数量
Set<K> keySet() 获取所有 key 组成的 Set 集合
Collection<V> values() 获取所有 value 组成的集合
Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象的集合

Map 实现类对比

实现类 底层结构 线程安全 特点
HashMap 哈希表(数组 + 链表 / 红黑树) 不安全 效率高,允许 key/value 为 null
LinkedHashMap 哈希表 + 双向链表 不安全 可保证插入顺序
TreeMap 红黑树 不安全 可对 key 自动排序
Hashtable 哈希表 安全 效率低,不允许 key/value 为 null(已不推荐)

image-20260727191106805

Map遍历方式

@Test
public void test55() {
    Map<String, String> map = new HashMap<>();
    map.put("cn", "中国");
    map.put("us", "美国");
    map.put("uk", "英国");
    String country = map.get("cn");
    System.out.println(country);

    // 方式1:遍历 entrySet(推荐,效率高)
    Set<Map.Entry<String, String>> entrySet = map.entrySet();
    for (Map.Entry<String, String> entry : entrySet) {
        System.out.println(entry.getKey() + " : " + entry.getValue());
    }

    // 方式2:遍历 iterator
    System.out.println("----------------");
    Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
    while (iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        System.out.println(entry.getKey() + " : " + entry.getValue());
    }

    // 方式3:遍历 key,通过 key 找 value
    System.out.println("-----------------");
    Set<String> keySet = map.keySet();
    for (String key : keySet) {
        System.out.println(key + " : " + map.get(key));
    }
    System.out.println("-----------------");
    
    // 方式4:Lambda 表达式(Java 8+)
    map.forEach((k, v) -> System.out.println(k + " → " + v));
    

}

###

集合遍历:迭代器 Iterator

Iterator 是 Java 集合的专用遍历工具,所有 Collection 集合都支持迭代器遍历。

常用方法

  • boolean hasNext():判断是否还有下一个元素
  • E next():获取下一个元素
  • void remove():删除当前元素

示例

List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("b".equals(s)) {
        it.remove(); // 迭代过程中删除元素,必须用迭代器的 remove
    }
}

⚠️ 注意:迭代过程中不能用集合自身的 add/remove 方法修改集合,否则会抛出并发修改异常 ConcurrentModificationException。

Collections 工具类

java.util.Collections 是集合的工具类,提供了大量静态方法操作集合。

表格

方法 作用
static void sort(List list) 对 List 集合自然排序
static void reverse(List list) 反转 List 集合元素
static void shuffle(List list) 随机打乱 List 集合元素
static int max(Collection coll) 获取集合中的最大值
static int min(Collection coll) 获取集合中的最小值
static boolean addAll(Collection c, T... elements) 批量添加多个元素

泛型基础

泛型(Generic)可以在编译期约束集合存储的数据类型,避免类型转换异常,让代码更安全。

基本使用

// 约束集合只能存 String 类型
List<String> list = new ArrayList<String>();
// Java 7+ 可简写为
List<String> list = new ArrayList<>();

泛型通配符

  • <?>:任意类型通配符
  • <? extends E>:上限通配符,只能是 E 或 E 的子类
  • <? super E>:下限通配符,只能是 E 或 E 的父类

集合选型建议

需要键值对 → 选 Map 体系

  • 排序需求用 TreeMap
  • 保证插入顺序用 LinkedHashMap
  • 普通场景用 HashMap

单列元素 → 选 Collection 体系

  • 可重复、需要索引用 List
  • 频繁查询用 ArrayList
  • 频繁首尾增删用 LinkedList
  • 不可重复用 Set
  • 去重即可用 HashSet
  • 保证插入顺序用 LinkedHashSet
  • 需要排序用 TreeSet

选大体系

  • 需要索引、允许重复 → 选 List
  • 需要去重、不允许重复 → 选 Set
  • 需要队列 / 栈、先进先出 / 后进先出 → 选 Queue/Deque

选具体实现

  • List 场景:
  • 频繁查询、少量增删 → ArrayList
  • 频繁首尾增删、做队列 / 栈 → LinkedList / ArrayDeque
  • 多线程读多写少 → CopyOnWriteArrayList
  • Set 场景:
  • 普通去重、无需顺序 → HashSet
  • 去重 + 保留插入顺序 → LinkedHashSet
  • 去重 + 自动排序 → TreeSet
  • 队列 / 栈场景:
  • 普通栈 / 队列 → ArrayDeque
  • 优先级排序 → PriorityQueue
  • 并发阻塞场景 → ArrayBlockingQueue
上一篇
下一篇