8、Java 集合框架体系深入详解

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.6k人参与

开篇概览:Java 集合框架整体架构

Java 集合框架(Java Collections Framework, JCF)是 Java 标准库中用于存储和操作一组对象的核心工具集。它提供了一套高性能、高复用、类型安全的接口与实现类,极大简化了数据结构的使用。

集合框架主要分为两大体系:

  1. Collection 接口体系:用于存储单个元素的集合;

    • List:有序、可重复(如 ArrayList, LinkedList);
    • Set:无序、不可重复(如 HashSet, TreeSet);
    • Queue:队列(本章暂不展开)。
  2. Map 接口体系:用于存储键值对(Key-Value)

    • HashMap, TreeMap, LinkedHashMap

此外,还包括:

  • 迭代器(Iterator):统一遍历集合;
  • 工具类(Collections):提供排序、查找、同步等静态方法。

掌握集合框架,不仅能高效处理数据,还能理解其底层数据结构(数组、链表、哈希表、红黑树等)的设计思想。


一、Collection 接口体系

1.1 Collection 接口核心方法

所有集合类(除 Map)都实现 java.util.Collection 接口,提供统一操作:

boolean add(E e);           // 添加元素
boolean remove(Object o);   // 删除元素
int size();                 // 元素个数
boolean isEmpty();          // 是否为空
boolean contains(Object o); // 是否包含某元素
Iterator<E> iterator();     // 获取迭代器
void clear();               // 清空集合

二、List 接口及其实现

List有序、可重复的集合,支持通过索引访问元素。

2.1 ArrayList(基于动态数组)

✅ 特点:
  • 底层结构Object[] 数组;
  • 查询快O(1)(通过索引直接访问);
  • 增删慢O(n)(需移动后续元素);
  • 线程不安全
  • 自动扩容:默认初始容量 10,扩容为原容量的 1.5 倍。
📌 底层原理详解:
  • 当添加元素超过当前数组容量时,调用 grow() 方法:
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍
    
  • 使用 System.arraycopy() 复制旧数组到新数组。
示例:ArrayList 使用
import java.util.*;

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建 ArrayList(初始容量默认为10)
        List<String> list = new ArrayList<>();

        // 添加元素(自动扩容)
        list.add("苹果");
        list.add("香蕉");
        list.add("橙子");
        System.out.println("列表内容: " + list); // [苹果, 香蕉, 橙子]

        // 通过索引访问
        System.out.println("第一个元素: " + list.get(0)); // 苹果

        // 在指定位置插入(后续元素后移)
        list.add(1, "葡萄");
        System.out.println("插入后: " + list); // [苹果, 葡萄, 香蕉, 橙子]

        // 删除元素(后续元素前移)
        list.remove("香蕉");
        System.out.println("删除后: " + list); // [苹果, 葡萄, 橙子]

        // 遍历(推荐使用增强 for 或 Iterator)
        for (String fruit : list) {
            System.out.println("水果: " + fruit);
        }
    }
}

2.2 LinkedList(基于双向链表)

✅ 特点:
  • 底层结构:双向链表(每个节点包含 prevnextitem);
  • 增删快O(1)(只需修改指针);
  • 查询慢O(n)(需从头/尾遍历);
  • 线程不安全
  • 额外方法addFirst()addLast()getFirst() 等(实现 Deque 接口)。
📌 底层原理详解:
  • 节点类定义:
    private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
    
  • 插入时只需调整前后节点指针,无需移动数据。
示例:LinkedList 使用
import java.util.*;

public class LinkedListDemo {
    public static void main(String[] args) {
        // 创建 LinkedList
        List<String> list = new LinkedList<>();

        // 添加元素
        list.add("A");
        list.add("B");
        list.add("C");

        // 使用 LinkedList 特有方法(需强转或声明为 LinkedList)
        LinkedList<String> linkedList = (LinkedList<String>) list;
        linkedList.addFirst("Head");   // 在头部添加
        linkedList.addLast("Tail");    // 在尾部添加

        System.out.println("链表内容: " + linkedList); // [Head, A, B, C, Tail]

        // 删除首尾元素
        System.out.println("移除头部: " + linkedList.removeFirst()); // Head
        System.out.println("移除尾部: " + linkedList.removeLast());  // Tail

        // 遍历
        for (String s : linkedList) {
            System.out.println("元素: " + s);
        }
    }
}
✅ ArrayList vs LinkedList 选择建议:
场景推荐
频繁随机访问(get/set)ArrayList
频繁在首尾增删LinkedList
内存敏感(LinkedList 节点多占内存)ArrayList

三、Set 接口及其实现

Set无序、不可重复的集合(基于 equals() 判断重复)。

3.1 HashSet(基于哈希表)

✅ 特点:
  • 底层结构HashMap(JDK 8+ 为 数组 + 链表/红黑树);
  • 无序:元素顺序与插入顺序无关;
  • 查找/添加/删除:平均 O(1)
  • 允许一个 null 元素
  • 线程不安全
📌 底层原理详解(JDK 8+):
  • 使用 HashMap<E, Object> 存储,key 为元素,value 为固定 PRESENT 对象;
  • 哈希冲突时:
    • 链表长度 < 8:使用链表;
    • 链表长度 ≥ 8 且数组长度 ≥ 64:转为红黑树(提升查找效率);
  • 扩容机制:当元素数量 > 容量 * 负载因子(默认 0.75) 时扩容为 2 倍。
示例:HashSet 使用
import java.util.*;

public class HashSetDemo {
    public static void main(String[] args) {
        // 创建 HashSet
        Set<String> set = new HashSet<>();

        // 添加元素(自动去重)
        set.add("Java");
        set.add("Python");
        set.add("Java"); // 重复,不会添加
        set.add(null);   // 允许一个 null

        System.out.println("集合大小: " + set.size()); // 3
        System.out.println("是否包含 Java: " + set.contains("Java")); // true

        // 遍历(顺序不确定)
        for (String lang : set) {
            System.out.println("编程语言: " + lang);
        }

        // 删除元素
        set.remove("Python");
        System.out.println("删除后: " + set);
    }
}

3.2 LinkedHashSet(基于 LinkedHashMap)

✅ 特点:
  • 底层结构LinkedHashMap(哈希表 + 双向链表);
  • 有序:按插入顺序迭代;
  • 性能略低于 HashSet(因维护链表);
  • 其他特性同 HashSet
示例:LinkedHashSet 使用
import java.util.*;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<>();

        set.add("第三");
        set.add("第一");
        set.add("第二");
        set.add("第一"); // 重复,忽略

        // 按插入顺序输出
        System.out.println("LinkedHashSet 顺序: " + set); // [第三, 第一, 第二]
    }
}

3.3 TreeSet(基于红黑树)

✅ 特点:
  • 底层结构红黑树(自平衡二叉搜索树)
  • 有序:按自然顺序自定义比较器排序;
  • 查找/添加/删除O(log n)
  • 不允许 null(除非自定义比较器处理);
  • 线程不安全
📌 红黑树特性:
  • 每个节点是红色或黑色;
  • 根节点是黑色;
  • 红色节点的子节点必须是黑色;
  • 从任一节点到其每个叶子的所有路径包含相同数目的黑色节点;
  • 保证树高度为 O(log n),操作高效。
示例:TreeSet 使用
import java.util.*;

public class TreeSetDemo {
    public static void main(String[] args) {
        // 1. 自然排序(实现 Comparable)
        Set<Integer> numbers = new TreeSet<>();
        numbers.add(5);
        numbers.add(1);
        numbers.add(3);
        System.out.println("数字排序: " + numbers); // [1, 3, 5]

        // 2. 自定义排序(Comparator)
        Set<String> words = new TreeSet<>((s1, s2) -> s2.compareTo(s1)); // 逆序
        words.add("Apple");
        words.add("Banana");
        words.add("Cherry");
        System.out.println("字符串逆序: " + words); // [Cherry, Banana, Apple]

        // 3. 自定义对象排序
        Set<Student> students = new TreeSet<>((s1, s2) -> Integer.compare(s1.age, s2.age));
        students.add(new Student("张三", 20));
        students.add(new Student("李四", 18));
        students.add(new Student("王五", 22));
        System.out.println("学生按年龄排序: " + students);
    }

    // 辅助类
    static class Student {
        String name;
        int age;
        Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString() {
            return name + "(" + age + ")";
        }
    }
}

四、Map 接口及其实现

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

4.1 HashMap(基于哈希表)

✅ 特点:
  • 底层结构:同 HashSet(数组 + 链表/红黑树);
  • 无序
  • 允许一个 null 键和多个 null
  • 线程不安全
📌 底层原理(JDK 8+):
  • 哈希函数:hash(key) = (key == null) ? 0 : h ^ (h >>> 16)(扰动函数,减少冲突);
  • 扩容:2 倍扩容,重新计算每个键的哈希桶位置;
  • 树化:链表长度 ≥ 8 且数组长度 ≥ 64 时转红黑树。
示例:HashMap 使用
import java.util.*;

public class HashMapDemo {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();

        // 添加键值对
        scores.put("张三", 95);
        scores.put("李四", 88);
        scores.put("王五", 92);
        scores.put(null, 100); // 允许 null 键

        // 获取值
        System.out.println("张三的成绩: " + scores.get("张三")); // 95
        System.out.println("null 键的值: " + scores.get(null)); // 100

        // 遍历(推荐 entrySet)
        for (Map.Entry<String, Integer> entry : scores.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }

        // 检查键/值
        System.out.println("是否包含李四: " + scores.containsKey("李四")); // true
        System.out.println("是否包含成绩90: " + scores.containsValue(90)); // false
    }
}

4.2 LinkedHashMap(哈希表 + 双向链表)

✅ 特点:
  • 按插入顺序或访问顺序迭代
  • 性能略低于 HashMap
  • 常用于 LRU 缓存(通过 accessOrder=true)。
示例:LinkedHashMap 使用
import java.util.*;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        // 按插入顺序
        Map<String, String> map = new LinkedHashMap<>();
        map.put("1", "一");
        map.put("3", "三");
        map.put("2", "二");
        System.out.println("插入顺序: " + map); // {1=一, 3=三, 2=二}

        // 按访问顺序(LRU)
        Map<String, String> lruMap = new LinkedHashMap<>(16, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
                return size() > 3; // 最多保留3个
            }
        };
        lruMap.put("A", "1");
        lruMap.put("B", "2");
        lruMap.put("C", "3");
        lruMap.get("A"); // 访问 A,将其移到末尾
        lruMap.put("D", "4"); // 触发移除最旧的 B
        System.out.println("LRU 顺序: " + lruMap); // {C=3, A=1, D=4}
    }
}

4.3 TreeMap(基于红黑树)

✅ 特点:
  • 按键的自然顺序或自定义比较器排序
  • 不允许 null(除非自定义比较器);
  • 提供范围操作subMap(), headMap(), tailMap()
示例:TreeMap 使用
import java.util.*;

public class TreeMapDemo {
    public static void main(String[] args) {
        Map<Integer, String> map = new TreeMap<>();

        map.put(3, "三");
        map.put(1, "一");
        map.put(2, "二");

        System.out.println("按键排序: " + map); // {1=一, 2=二, 3=三}

        // 范围查询
        System.out.println("小于2的键值对: " + map.headMap(2)); // {1=一}
        System.out.println("大于等于2的键值对: " + map.tailMap(2)); // {2=二, 3=三}
    }
}

五、迭代器 Iterator

用于安全遍历集合,支持在遍历时删除元素(remove() 方法)。

示例:Iterator 使用
import java.util.*;

public class IteratorDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        // 获取迭代器
        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            String item = it.next();
            if ("B".equals(item)) {
                it.remove(); // 安全删除(避免 ConcurrentModificationException)
            }
        }
        System.out.println("删除 B 后: " + list); // [A, C]

        // 增强 for 循环本质是 Iterator
        for (String s : list) {
            System.out.println(s);
        }
    }
}

⚠️ 注意:遍历时直接调用集合的 remove() 会抛出 ConcurrentModificationException


六、工具类 Collections

提供对集合的静态操作方法

示例:Collections 常用方法
import java.util.*;

public class CollectionsDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(1);
        list.add(2);

        // 排序
        Collections.sort(list);
        System.out.println("排序后: " + list); // [1, 2, 3]

        // 反转
        Collections.reverse(list);
        System.out.println("反转后: " + list); // [3, 2, 1]

        // 打乱顺序
        Collections.shuffle(list);
        System.out.println("打乱后: " + list);

        // 查找最大/最小值
        System.out.println("最大值: " + Collections.max(list)); // 3
        System.out.println("最小值: " + Collections.min(list)); // 1

        // 创建不可变集合(Java 9+ 推荐用 List.of())
        List<String> unmodifiable = Collections.unmodifiableList(Arrays.asList("A", "B"));
        // unmodifiable.add("C"); // ❌ 抛出 UnsupportedOperationException

        // 同步包装(线程安全,但性能低)
        List<Integer> syncList = Collections.synchronizedList(new ArrayList<>());
    }
}

七、总结:集合选型指南

需求推荐集合
有序、可重复、随机访问多ArrayList
频繁首尾增删LinkedList
去重、无序、高性能HashSet
去重、插入顺序LinkedHashSet
去重、排序TreeSet
键值对、无序、高性能HashMap
键值对、插入顺序LinkedHashMap
键值对、按键排序TreeMap

📌 核心原则

  • 默认使用 ArrayListHashMap
  • 需要排序用 TreeSet/TreeMap
  • 需要插入顺序用 LinkedHashSet/LinkedHashMap
  • 避免在遍历时直接修改集合,使用 Iterator.remove()

掌握集合框架的接口设计、实现原理与适用场景,是编写高效 Java 程序的关键能力。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值