强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

14 - 集合框架:List、Set、Map、Queue、Iterator

14 - 集合框架:List、Set、Map、Queue、Iterator

集合框架全景

Iterable
├── Collection
│   ├── List(有序,可重复)
│   │   ├── ArrayList
│   │   ├── LinkedList
│   │   └── Vector / Stack
│   ├── Set(无序,不重复)
│   │   ├── HashSet
│   │   ├── LinkedHashSet
│   │   └── TreeSet
│   └── Queue(队列)
│       ├── LinkedList
│       ├── PriorityQueue
│       └── ArrayDeque
└── Map(键值对)
    ├── HashMap
    ├── LinkedHashMap
    ├── TreeMap
    ├── Hashtable
    └── ConcurrentHashMap

List — 有序可重复

ArrayList

import java.util.*;

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建
        List<String> list = new ArrayList<>();
        List<String> fixed = List.of("A", "B", "C");     // 不可变
        List<String> copied = List.copyOf(fixed);         // 不可变副本

        // 添加
        list.add("张三");
        list.add("李四");
        list.add(0, "王五");  // 指定位置插入
        list.addAll(List.of("赵六", "钱七"));

        // 访问
        System.out.println(list.get(0));       // 王五
        System.out.println(list.size());       // 5
        System.out.println(list.indexOf("李四")); // 2
        System.out.println(list.contains("张三")); // true

        // 修改
        list.set(0, "老王");

        // 删除
        list.remove("李四");         // 按对象删除
        list.remove(0);              // 按索引删除
        list.removeIf(s -> s.startsWith("赵"));  // 条件删除

        // 排序
        list.sort(Comparator.naturalOrder());
        list.sort(Comparator.reverseOrder());
        list.sort(Comparator.comparingInt(String::length));

        // 遍历
        for (String s : list) System.out.println(s);
        list.forEach(System.out::println);

        // 转换
        String[] arr = list.toArray(new String[0]);
        List<String> sub = list.subList(0, 2);  // 视图,非副本
    }
}

ArrayList vs LinkedList

维度ArrayListLinkedList
底层动态数组双向链表
随机访问O(1)O(n)
头部插入/删除O(n)O(1)
尾部插入O(1)O(1)
内存紧凑每个节点额外开销
推荐场景通用首选频繁头部操作

💡 绝大多数场景使用 ArrayList,它是性能最好的通用 List 实现。

Set — 无序不重复

import java.util.*;

public class SetDemo {
    public static void main(String[] args) {
        // HashSet —— 基于 HashMap,O(1) 操作
        Set<String> hashSet = new HashSet<>();
        hashSet.add("张三");
        hashSet.add("李四");
        hashSet.add("张三");  // 重复,不添加
        System.out.println(hashSet.size());  // 2
        System.out.println(hashSet.contains("张三")); // true

        // LinkedHashSet —— 保持插入顺序
        Set<String> linkedSet = new LinkedHashSet<>();
        linkedSet.add("C"); linkedSet.add("A"); linkedSet.add("B");
        System.out.println(linkedSet);  // [C, A, B]

        // TreeSet —— 自然排序,红黑树实现
        Set<String> treeSet = new TreeSet<>();
        treeSet.add("C"); treeSet.add("A"); treeSet.add("B");
        System.out.println(treeSet);  // [A, B, C]

        // 集合运算
        Set<Integer> a = Set.of(1, 2, 3, 4);
        Set<Integer> b = Set.of(3, 4, 5, 6);

        // 并集
        Set<Integer> union = new HashSet<>(a);
        union.addAll(b);
        System.out.println("并集: " + union);  // [1, 2, 3, 4, 5, 6]

        // 交集
        Set<Integer> intersection = new HashSet<>(a);
        intersection.retainAll(b);
        System.out.println("交集: " + intersection);  // [3, 4]

        // 差集
        Set<Integer> diff = new HashSet<>(a);
        diff.removeAll(b);
        System.out.println("差集: " + diff);  // [1, 2]
    }
}

Map — 键值对

import java.util.*;

public class MapDemo {
    public static void main(String[] args) {
        // 创建
        Map<String, Integer> scores = new HashMap<>();
        scores.put("张三", 90);
        scores.put("李四", 85);
        scores.put("王五", 92);

        // 访问
        int zhangScore = scores.get("张三");           // 90
        int defaultScore = scores.getOrDefault("赵六", 0);  // 0
        System.out.println("包含张三: " + scores.containsKey("张三"));
        System.out.println("包含90: " + scores.containsValue(90));

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

        // Java 8 新方法
        scores.putIfAbsent("赵六", 88);  // 不存在才添加
        scores.merge("张三", 5, Integer::sum);  // 张三: 90+5=95
        scores.compute("李四", (k, v) -> v == null ? 0 : v + 10); // 李四: 95

        // 排序
        Map<String, Integer> sorted = new TreeMap<>(scores);  // 按 key 排序
        scores.entrySet().stream()
            .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
            .forEach(e -> System.out.println(e.getKey() + ": " + e.getValue()));

        // 不可变 Map
        Map<String, Integer> immutable = Map.of("A", 1, "B", 2, "C", 3);
        Map<String, Integer> copied = Map.copyOf(scores);
    }
}

Map 实现对比

实现有序线程安全null 键适用场景
HashMap通用首选
LinkedHashMap插入序需要保持插入顺序
TreeMap排序序需要 key 排序
Hashtable遗留,不推荐
ConcurrentHashMap并发场景首选

Queue & Deque

import java.util.*;

public class QueueDemo {
    public static void main(String[] args) {
        // ArrayDeque —— 双端队列(推荐作栈和队列)
        Deque<String> deque = new ArrayDeque<>();
        deque.offerFirst("A");  // 头部入队
        deque.offerLast("B");   // 尾部入队
        deque.offerLast("C");
        System.out.println(deque.peekFirst());  // A
        System.out.println(deque.peekLast());   // C
        deque.pollFirst();  // 移除 A

        // 用作栈
        Deque<String> stack = new ArrayDeque<>();
        stack.push("第一层");
        stack.push("第二层");
        stack.push("第三层");
        System.out.println(stack.pop());   // 第三层(LIFO)
        System.out.println(stack.peek());  // 第二层

        // PriorityQueue —— 优先队列(最小堆)
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        minHeap.offer(5); minHeap.offer(1); minHeap.offer(3);
        System.out.println(minHeap.poll());  // 1(最小值先出)
        System.out.println(minHeap.poll());  // 3

        // 最大堆
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        maxHeap.offer(5); maxHeap.offer(1); maxHeap.offer(3);
        System.out.println(maxHeap.poll());  // 5(最大值先出)
    }
}

不可变集合(JDK 9+)

import java.util.*;

public class ImmutableCollections {
    public static void main(String[] args) {
        // List 不可变
        List<String> list = List.of("A", "B", "C");
        // list.add("D");  // ❌ UnsupportedOperationException

        // Set 不可变
        Set<Integer> set = Set.of(1, 2, 3);

        // Map 不可变(最多 10 个键值对)
        Map<String, Integer> map = Map.of("one", 1, "two", 2, "three", 3);

        // 更多键值对用 Map.ofEntries
        Map<String, Integer> bigMap = Map.ofEntries(
            Map.entry("A", 1),
            Map.entry("B", 2),
            Map.entry("C", 3)
        );

        // 从可变集合创建不可变副本
        List<String> mutable = new ArrayList<>(list);
        List<String> immutable = List.copyOf(mutable);
    }
}

集合选型指南

需求推荐说明
有序列表,按索引访问ArrayList通用首选
频繁头部增删LinkedList链表结构
不重复集合HashSet去重首选
不重复且排序TreeSet红黑树
键值对HashMap通用首选
键值对且排序TreeMap按 key 排序
并发安全 MapConcurrentHashMap分段锁
FIFO 队列ArrayDeque双端队列
优先级队列PriorityQueue堆结构

⚠️ 注意事项

  1. 线程不安全ArrayListHashMap 非线程安全,并发环境用 ConcurrentHashMapCollections.synchronizedList()
  2. ConcurrentModificationException — 遍历时不要直接 remove(),用 Iterator.remove()removeIf()
  3. HashMap 的 key 需要重写 equalshashCode — 否则无法正确去重。
  4. Arrays.asList() 返回的列表不可增删 — 底层仍是数组。

💡 技巧

  1. Map 计数器

    Map<String, Integer> counter = new HashMap<>();
    words.forEach(w -> counter.merge(w, 1, Integer::sum));
    
  2. Map computeIfAbsent

    Map<String, List<String>> groups = new HashMap<>();
    items.forEach(item -> 
        groups.computeIfAbsent(item.getCategory(), k -> new ArrayList<>()).add(item));
    
  3. List 转 Map

    Map<Long, User> userMap = users.stream()
        .collect(Collectors.toMap(User::getId, u -> u));
    

🏢 业务场景

  • 缓存: HashMap/ConcurrentHashMap 实现本地缓存。
  • 排行榜: TreeMapPriorityQueue 实现 Top N。
  • 分组统计: Map<String, List<T>> 按字段分组。
  • BFS/DFS: Queue + Set 实现图的遍历。

📖 扩展阅读