Skip to content

集合框架

Java 集合框架提供了一套性能优良、使用方便的接口和类,用于存储和操作对象集合。

集合框架概述

集合体系结构

text
┌─────────────────────────────────────────────────────────────────┐
│                     Java 集合框架                                │
├─────────────────────────────────────────────────────────────────┤
│                        Iterable                                 │
│                           │                                     │
│                       Collection                                │
│                      /     |     \                              │
│                   List    Set    Queue                          │
│                   / \      |      |                             │
│             ArrayList  HashSet  LinkedList                      │
│            LinkedList  TreeSet  PriorityQueue                   │
│             Vector     LinkedHashSet                            │
│                                                                 │
│                        Map(独立接口)                            │
│                      /     |     \                              │
│                 HashMap  TreeMap  LinkedHashMap                 │
│                Hashtable                                       │
└─────────────────────────────────────────────────────────────────┘

集合分类

类型特点主要实现类
List有序、可重复ArrayList, LinkedList, Vector
Set无序、不可重复HashSet, TreeSet, LinkedHashSet
Map键值对、键唯一HashMap, TreeMap, LinkedHashMap
Queue先进先出LinkedList, PriorityQueue

Collection 接口

Collection 是单值集合的根接口。

常用方法

java
import java.util.*;

public class CollectionMethods {
    public static void main(String[] args) {
        Collection<String> collection = new ArrayList<>();
        
        // 添加元素
        collection.add("张三");
        collection.add("李四");
        collection.add("王五");
        System.out.println("添加后:" + collection);
        
        // 添加多个元素
        collection.addAll(Arrays.asList("赵六", "钱七"));
        System.out.println("添加多个后:" + collection);
        
        // 判断是否包含
        System.out.println("包含张三:" + collection.contains("张三"));
        System.out.println("包含所有:" + collection.containsAll(Arrays.asList("张三", "李四")));
        
        // 大小和判空
        System.out.println("大小:" + collection.size());
        System.out.println("是否为空:" + collection.isEmpty());
        
        // 删除元素
        collection.remove("张三");
        System.out.println("删除张三后:" + collection);
        
        // 遍历
        System.out.println("\n遍历方式一:增强 for");
        for (String name : collection) {
            System.out.println(name);
        }
        
        System.out.println("\n遍历方式二:Iterator");
        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
        
        System.out.println("\n遍历方式三:forEach + Lambda");
        collection.forEach(System.out::println);
        
        // 清空
        collection.clear();
        System.out.println("清空后:" + collection);
    }
}

List 接口

List 是有序集合,可以存储重复元素,可以通过索引访问。

ArrayList

ArrayList 基于动态数组实现,查询快,增删慢。

java
import java.util.*;

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建 ArrayList
        List<String> list = new ArrayList<>();
        
        // 添加元素
        list.add("Java");
        list.add("Python");
        list.add("C++");
        list.add("JavaScript");
        System.out.println("初始列表:" + list);
        
        // 在指定位置插入
        list.add(1, "Go");
        System.out.println("插入后:" + list);
        
        // 获取元素
        System.out.println("索引0的元素:" + list.get(0));
        
        // 修改元素
        list.set(0, "Java SE");
        System.out.println("修改后:" + list);
        
        // 查找元素
        System.out.println("Python 的索引:" + list.indexOf("Python"));
        System.out.println("最后出现的位置:" + list.lastIndexOf("Python"));
        
        // 截取子列表
        List<String> subList = list.subList(1, 3);
        System.out.println("子列表:" + subList);
        
        // 排序
        list.sort(String::compareTo);
        System.out.println("排序后:" + list);
        
        // 遍历方式
        System.out.println("\n使用普通 for 循环:");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(i + " -> " + list.get(i));
        }
        
        System.out.println("\n使用 ListIterator(双向遍历):");
        ListIterator<String> listIterator = list.listIterator(list.size());
        while (listIterator.hasPrevious()) {
            System.out.println(listIterator.previousIndex() + " -> " + listIterator.previous());
        }
    }
}

LinkedList

LinkedList 基于双向链表实现,增删快,查询慢。

java
import java.util.*;

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();
        
        // 添加元素
        list.add("A");
        list.add("B");
        list.add("C");
        System.out.println("初始列表:" + list);
        
        // 在头部添加
        list.addFirst("First");
        System.out.println("头部添加后:" + list);
        
        // 在尾部添加
        list.addLast("Last");
        System.out.println("尾部添加后:" + list);
        
        // 获取头部和尾部
        System.out.println("头部元素:" + list.getFirst());
        System.out.println("尾部元素:" + list.getLast());
        
        // 删除头部和尾部
        list.removeFirst();
        list.removeLast();
        System.out.println("删除头尾后:" + list);
        
        // 栈操作(后进先出)
        list.push("X");  // 等同于 addFirst
        list.push("Y");
        System.out.println("push 后:" + list);
        
        System.out.println("pop:" + list.pop());  // 等同于 removeFirst
        System.out.println("pop 后:" + list);
        
        // 队列操作(先进先出)
        LinkedList<String> queue = new LinkedList<>();
        queue.offer("1");  // 添加到尾部
        queue.offer("2");
        queue.offer("3");
        System.out.println("\n队列:" + queue);
        System.out.println("poll:" + queue.poll());  // 从头部取出
        System.out.println("poll 后:" + queue);
    }
}

ArrayList vs LinkedList

text
┌─────────────────────────────────────────────────────────────────┐
│                  ArrayList vs LinkedList                        │
├─────────────────────────────────────────────────────────────────┤
│      操作      │    ArrayList    │    LinkedList               │
├─────────────────────────────────────────────────────────────────┤
│   随机访问     │      O(1)       │      O(n)                   │
│   头部插入     │      O(n)       │      O(1)                   │
│   尾部插入     │      O(1)       │      O(1)                   │
│   中间插入     │      O(n)       │      O(n)                   │
│   内存占用     │      较小       │      较大                    │
│   适用场景     │    查询多       │    增删多                    │
└─────────────────────────────────────────────────────────────────┘

Set 接口

Set 是不包含重复元素的集合。

HashSet

HashSet 基于哈希表实现,元素无序,查询效率高。

java
import java.util.*;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        
        // 添加元素
        set.add("Java");
        set.add("Python");
        set.add("C++");
        set.add("Java");  // 重复元素不会被添加
        System.out.println("HashSet:" + set);  // 顺序不确定
        
        // 判断包含
        System.out.println("包含 Java:" + set.contains("Java"));
        
        // 删除元素
        set.remove("C++");
        System.out.println("删除后:" + set);
        
        // 遍历
        System.out.println("\n遍历:");
        for (String item : set) {
            System.out.println(item);
        }
        
        // 集合操作
        Set<String> set1 = new HashSet<>(Arrays.asList("A", "B", "C"));
        Set<String> set2 = new HashSet<>(Arrays.asList("B", "C", "D"));
        
        // 并集
        Set<String> union = new HashSet<>(set1);
        union.addAll(set2);
        System.out.println("\n并集:" + union);
        
        // 交集
        Set<String> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);
        System.out.println("交集:" + intersection);
        
        // 差集
        Set<String> difference = new HashSet<>(set1);
        difference.removeAll(set2);
        System.out.println("差集:" + difference);
    }
}

LinkedHashSet

LinkedHashSet 维护元素的插入顺序。

java
import java.util.*;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        // LinkedHashSet:维护插入顺序
        Set<String> linkedSet = new LinkedHashSet<>();
        linkedSet.add("C");
        linkedSet.add("A");
        linkedSet.add("B");
        System.out.println("LinkedHashSet:" + linkedSet);  // [C, A, B]
        
        // HashSet:不保证顺序
        Set<String> hashSet = new HashSet<>();
        hashSet.add("C");
        hashSet.add("A");
        hashSet.add("B");
        System.out.println("HashSet:" + hashSet);  // 顺序不确定
    }
}

TreeSet

TreeSet 基于红黑树实现,元素有序(自然排序或自定义排序)。

java
import java.util.*;

public class TreeSetDemo {
    public static void main(String[] args) {
        // 自然排序
        TreeSet<Integer> numbers = new TreeSet<>();
        numbers.add(5);
        numbers.add(2);
        numbers.add(8);
        numbers.add(1);
        System.out.println("自然排序:" + numbers);  // [1, 2, 5, 8]
        
        // 特定方法
        System.out.println("第一个元素:" + numbers.first());
        System.out.println("最后一个元素:" + numbers.last());
        System.out.println("小于5的最大元素:" + numbers.lower(5));
        System.out.println("大于5的最小元素:" + numbers.higher(5));
        
        // 范围操作
        System.out.println("小于等于5的元素:" + numbers.headSet(5, true));
        System.out.println("大于等于2的元素:" + numbers.tailSet(2));
        System.out.println("2到5之间的元素:" + numbers.subSet(2, 5));
        
        // 自定义排序(降序)
        TreeSet<Integer> descending = new TreeSet<>(Comparator.reverseOrder());
        descending.addAll(numbers);
        System.out.println("降序排序:" + descending);
        
        // 自定义对象排序
        TreeSet<Person> people = new TreeSet<>((p1, p2) -> p1.age - p2.age);
        people.add(new Person("张三", 25));
        people.add(new Person("李四", 20));
        people.add(new Person("王五", 30));
        System.out.println("\n按年龄排序:");
        people.forEach(p -> System.out.println(p.name + " - " + p.age));
    }
}

class Person {
    String name;
    int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Map 接口

Map 存储键值对,键唯一,值可重复。

HashMap

HashMap 基于哈希表实现,键值对无序,查询效率高。

java
import java.util.*;

public class HashMapDemo {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        
        // 添加键值对
        map.put("张三", 25);
        map.put("李四", 30);
        map.put("王五", 28);
        System.out.println("初始 Map:" + map);
        
        // 获取值
        System.out.println("张三的年龄:" + map.get("张三"));
        System.out.println("不存在的键:" + map.get("赵六"));  // null
        System.out.println("不存在返回默认值:" + map.getOrDefault("赵六", 0));
        
        // 判断包含
        System.out.println("包含张三键:" + map.containsKey("张三"));
        System.out.println("包含25值:" + map.containsValue(25));
        
        // 修改值
        map.put("张三", 26);  // 键相同会覆盖
        System.out.println("修改后:" + map);
        
        // 如果键不存在才添加
        map.putIfAbsent("张三", 27);  // 不会添加
        map.putIfAbsent("赵六", 22);  // 会添加
        System.out.println("putIfAbsent 后:" + map);
        
        // 删除
        map.remove("赵六");
        System.out.println("删除后:" + map);
        
        // 遍历方式
        System.out.println("\n遍历方式一:遍历键");
        for (String key : map.keySet()) {
            System.out.println(key + " -> " + map.get(key));
        }
        
        System.out.println("\n遍历方式二:遍历值");
        for (Integer value : map.values()) {
            System.out.println(value);
        }
        
        System.out.println("\n遍历方式三:遍历键值对");
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " -> " + entry.getValue());
        }
        
        System.out.println("\n遍历方式四:forEach");
        map.forEach((k, v) -> System.out.println(k + " -> " + v));
        
        // 计算方法
        map.merge("张三", 1, Integer::sum);  // 如果存在则合并
        System.out.println("\nmerge 后:" + map);
        
        map.compute("李四", (k, v) -> v + 1);  // 计算新值
        System.out.println("compute 后:" + map);
    }
}

LinkedHashMap

LinkedHashMap 维护键值对的插入顺序或访问顺序。

java
import java.util.*;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        // 插入顺序
        Map<String, Integer> insertOrder = new LinkedHashMap<>();
        insertOrder.put("C", 3);
        insertOrder.put("A", 1);
        insertOrder.put("B", 2);
        System.out.println("插入顺序:" + insertOrder);
        
        // 访问顺序(可用于实现 LRU 缓存)
        Map<String, Integer> accessOrder = new LinkedHashMap<>(16, 0.75f, true);
        accessOrder.put("A", 1);
        accessOrder.put("B", 2);
        accessOrder.put("C", 3);
        System.out.println("初始:" + accessOrder);
        
        accessOrder.get("A");  // 访问 A
        accessOrder.get("B");  // 访问 B
        System.out.println("访问后:" + accessOrder);  // A 和 B 移到最后
    }
}

TreeMap

TreeMap 基于红黑树实现,键有序。

java
import java.util.*;

public class TreeMapDemo {
    public static void main(String[] args) {
        // 自然排序
        TreeMap<String, Integer> map = new TreeMap<>();
        map.put("C", 3);
        map.put("A", 1);
        map.put("B", 2);
        System.out.println("TreeMap:" + map);  // 按键排序
        
        // 范围操作
        System.out.println("第一个键:" + map.firstKey());
        System.out.println("最后一个键:" + map.lastKey());
        System.out.println("小于B的键值对:" + map.headMap("B"));
        System.out.println("大于等于B的键值对:" + map.tailMap("B"));
        System.out.println("A到C之间的键值对:" + map.subMap("A", "C"));
    }
}

Collections 工具类

Collections 提供了操作集合的静态方法。

java
import java.util.*;

public class CollectionsDemo {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3));
        
        // 排序
        Collections.sort(list);
        System.out.println("升序排序:" + list);
        
        Collections.sort(list, Comparator.reverseOrder());
        System.out.println("降序排序:" + list);
        
        // 二分查找(需要先排序)
        Collections.sort(list);
        int index = Collections.binarySearch(list, 5);
        System.out.println("5 的索引:" + index);
        
        // 最大最小值
        System.out.println("最大值:" + Collections.max(list));
        System.out.println("最小值:" + Collections.min(list));
        
        // 反转
        Collections.reverse(list);
        System.out.println("反转后:" + list);
        
        // 打乱
        Collections.shuffle(list);
        System.out.println("打乱后:" + list);
        
        // 填充
        List<String> names = new ArrayList<>(Arrays.asList("A", "B", "C"));
        Collections.fill(names, "X");
        System.out.println("填充后:" + names);
        
        // 复制
        List<String> dest = Arrays.asList(new String[3]);
        List<String> src = Arrays.asList("1", "2", "3");
        Collections.copy(dest, src);
        System.out.println("复制后:" + dest);
        
        // 不可变集合
        List<String> immutable = Collections.unmodifiableList(Arrays.asList("A", "B"));
        System.out.println("不可变集合:" + immutable);
        // immutable.add("C");  // UnsupportedOperationException
        
        // 同步集合(线程安全)
        List<String> syncList = Collections.synchronizedList(new ArrayList<>());
        Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());
        Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());
        
        // 空集合
        List<String> emptyList = Collections.emptyList();
        Set<String> emptySet = Collections.emptySet();
        Map<String, String> emptyMap = Collections.emptyMap();
        
        // 单元素集合
        List<String> singletonList = Collections.singletonList("Only");
        Set<String> singletonSet = Collections.singleton("Only");
    }
}

集合选择

text
┌─────────────────────────────────────────────────────────────────┐
│                      集合选择指南                                │
├─────────────────────────────────────────────────────────────────┤
│  需要快速随机访问元素?                                          │
│    是 → ArrayList                                               │
│    否 → 需要频繁在头部插入删除?                                  │
│           是 → LinkedList                                       │
│           否 → ArrayList                                        │
│                                                                 │
│  需要存储不重复元素?                                            │
│    是 → 需要排序?                                               │
│           是 → TreeSet                                          │
│           否 → 需要保持插入顺序?                                 │
│                  是 → LinkedHashSet                             │
│                  否 → HashSet                                   │
│                                                                 │
│  需要键值对存储?                                                │
│    是 → 需要按键排序?                                           │
│           是 → TreeMap                                          │
│           否 → 需要保持插入顺序?                                 │
│                  是 → LinkedHashMap                             │
│                  否 → HashMap                                   │
└─────────────────────────────────────────────────────────────────┘

小结

本章我们学习了:

  • Collection 接口:单值集合的根接口
  • List 接口:有序可重复,ArrayList、LinkedList
  • Set 接口:无序不可重复,HashSet、TreeSet、LinkedHashSet
  • Map 接口:键值对,HashMap、TreeMap、LinkedHashMap
  • Collections 工具类:排序、查找、同步等操作

下一章,我们将学习 IO流,了解 Java 的输入输出操作。