08 Java集合类

72 阅读7分钟

Java集合类

Java 集合类是用于存储和操作多个对象的容器,位于 java.util 包下,主要分为 Collection 和 Map 两大体系,提供了丰富的数据结构和操作方法。以下是核心内容整理:

一、集合体系概览

  • Collection:存储单个元素的集合,主要子接口包括 List(有序可重复)、Set(无序不可重复)、Queue(队列,FIFO)。
  • Map:存储键值对(Key-Value)的集合,键唯一,值可重复。

二、Collection 接口核心实现类

1. List 接口(有序、可重复)

ArrayList

  • 底层基于动态数组实现,查询快(索引访问),增删慢(需移动元素)。
  • 初始容量 10,扩容时默认增长 50%(JDK 1.8+)。
  • 线程不安全,效率高。
import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建ArrayList(默认存10个元素,满了自动扩容)
        List<String> fruits = new ArrayList<>();

        // 添加元素
        fruits.add("苹果");
        fruits.add("香蕉");
        fruits.add("苹果"); // 允许重复

        // 按索引获取元素(查询快)
        System.out.println(fruits.get(0)); // 输出:苹果

        // 遍历元素
        for (String fruit : fruits) {
            System.out.println(fruit); // 顺序:苹果、香蕉、苹果
        }

        // 删除元素(会挪动后续元素,效率低)
        fruits.remove(1); // 删除索引1的"香蕉"
    }
}

LinkedList

  • 底层基于双向链表实现,增删快(修改指针),查询慢(需遍历)。
  • 实现了 Deque 接口,可作为队列 / 栈使用。
  • 线程不安全。
import java.util.LinkedList;
import java.util.List;

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

        // 添加元素(增删快)
        numbers.add(10);
        numbers.add(20);
        numbers.addFirst(5); // 头部添加
        numbers.addLast(30); // 尾部添加

        // 遍历
        for (int num : numbers) {
            System.out.println(num); // 输出:5、10、20、30
        }

        // 作为栈使用(先进后出)
        LinkedList<String> stack = new LinkedList<>();
        stack.push("A"); // 入栈
        stack.push("B");
        System.out.println(stack.pop()); // 出栈:B
    }
}

Vector

  • 与 ArrayList 类似,但线程安全(方法加 synchronized),效率低,已被弃用(推荐用 Collections.synchronizedList 替代)。
import java.util.Vector;
import java.util.Enumeration;

public class VectorDemo {
    public static void main(String[] args) {
        // 创建Vector(初始容量10,扩容默认翻倍,线程安全)
        Vector<String> vector = new Vector<>();

        // 添加元素(add方法线程安全,带synchronized修饰)
        vector.add("元素1");
        vector.add("元素2");
        vector.add("元素3");

        // 指定索引添加
        vector.add(1, "插入的元素");

        // 获取元素(按索引访问,类似ArrayList)
        System.out.println("索引0的元素:" + vector.get(0)); // 输出:元素1

        // 遍历方式1:普通for循环
        System.out.println("\n普通for循环遍历:");
        for (int i = 0; i < vector.size(); i++) {
            System.out.println(vector.get(i));
        }

        // 遍历方式2:迭代器(Enumeration,Vector特有的遍历器)
        System.out.println("\nEnumeration遍历:");
        Enumeration<String> elements = vector.elements();
        while (elements.hasMoreElements()) {
            System.out.println(elements.nextElement());
        }

        // 修改元素
        vector.set(0, "修改后的元素1");
        System.out.println("\n修改后索引0的元素:" + vector.get(0));

        // 删除元素
        vector.remove(2); // 删除索引2的元素
        System.out.println("\n删除后元素数量:" + vector.size());

        // 判断是否包含某个元素
        System.out.println("是否包含'元素2':" + vector.contains("元素2"));
    }
}
2. Set 接口(无序、不可重复,基于 equals() 和 hashCode() 判断)

HashSet

  • 底层基于哈希表(HashMap)实现,存储无序,查询 / 增删效率高(O (1))。
  • 元素需重写 hashCode() 和 equals() 保证唯一性。
import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        // 创建HashSet(自动去重)
        Set<String> names = new HashSet<>();

        names.add("张三");
        names.add("李四");
        names.add("张三"); // 重复元素,不会被存入

        // 遍历(无序)
        for (String name : names) {
            System.out.println(name); // 可能输出:李四、张三(顺序不确定)
        }
    }
}

LinkedHashSet

  • 继承 HashSet,底层多了双向链表记录插入顺序,兼具哈希表的高效和顺序性。
import java.util.LinkedHashSet;
import java.util.Set;

public class LinkedHashSetDemo {
    public static void main(String[] args) {
        // 创建LinkedHashSet(去重 + 保留插入顺序)
        Set<String> books = new LinkedHashSet<>();

        // 添加元素
        books.add("Java编程思想");
        books.add("深入理解Java虚拟机");
        books.add("Java并发编程实战");
        books.add("Java编程思想"); // 重复元素,不会被存入

        // 遍历集合(按插入顺序输出,与HashSet的无序不同)
        System.out.println("LinkedHashSet遍历(插入顺序):");
        for (String book : books) {
            System.out.println(book);
        }
        // 输出顺序:
        // Java编程思想
        // 深入理解Java虚拟机
        // Java并发编程实战

        // 常用方法演示
        System.out.println("\n是否包含《深入理解Java虚拟机》:" + books.contains("深入理解Java虚拟机")); // true
        System.out.println("集合大小:" + books.size()); // 3

        // 删除元素
        books.remove("Java并发编程实战");
        System.out.println("\n删除后剩余元素:");
        for (String book : books) {
            System.out.println(book);
        }
        // 输出:
        // Java编程思想
        // 深入理解Java虚拟机
    }
}

TreeSet

  • 底层基于红黑树实现,元素自动排序(自然排序或自定义 Comparator)。
  • 查询 / 增删效率 O (log n),需元素可比较(实现 Comparable 接口)。
import java.util.TreeSet;
import java.util.Set;

public class TreeSetDemo {
    public static void main(String[] args) {
        // 创建TreeSet(自动按自然顺序排序)
        Set<Integer> nums = new TreeSet<>();

        nums.add(30);
        nums.add(10);
        nums.add(20);

        // 遍历(已排序)
        for (int num : nums) {
            System.out.println(num); // 输出:10、20、30
        }
    }
}
3. Queue 接口(队列,先进先出)

ArrayDeque

  • 基于动态数组的双端队列,效率高于 LinkedList,可作为栈(push/pop)使用。
import java.util.ArrayDeque;
import java.util.Queue;

public class ArrayDequeDemo {
    public static void main(String[] args) {
        // 作为队列(先进先出)
        Queue<String> queue = new ArrayDeque<>();
        queue.offer("第一个人"); // 入队
        queue.offer("第二个人");
        System.out.println(queue.poll()); // 出队:第一个人

        // 作为栈(先进后出)
        ArrayDeque<String> stack = new ArrayDeque<>();
        stack.push("A");
        stack.push("B");
        System.out.println(stack.pop()); // 出栈:B
    }
}

PriorityQueue

  • 优先级队列,元素按优先级排序(默认小顶堆),需元素可比较。
import java.util.PriorityQueue;
import java.util.Queue;

public class PriorityQueueDemo {
    public static void main(String[] args) {
        // 创建PriorityQueue(默认按自然顺序排序,小顶堆:每次取出最小元素)
        Queue<Integer> pq = new PriorityQueue<>();

        // 添加元素
        pq.offer(30);
        pq.offer(10);
        pq.offer(20);
        pq.offer(5);

        // 优先级队列的特点:取出元素时按优先级(从小到大)
        System.out.println("按优先级取出元素:");
        while (!pq.isEmpty()) {
            System.out.println(pq.poll()); // 每次取出最小的元素
        }
        // 输出顺序:5 → 10 → 20 → 30
    }
}

三、Map 接口核心实现类(键值对存储)

HashMap(JDK 1.8+)

  • 底层:数组 + 链表(当链表长度 > 8 时转为红黑树,< 6 时退化为链表)。
  • 特点:键无序,允许键为 null(仅一个),值可 null;线程不安全,效率高。
  • 初始容量 16,负载因子 0.75,扩容时容量翻倍。
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建HashMap(键值对,键唯一)
        Map<String, Integer> scoreMap = new HashMap<>();

        // 添加键值对
        scoreMap.put("张三", 90);
        scoreMap.put("李四", 85);
        scoreMap.put("张三", 95); // 键重复,会覆盖值

        // 获取值
        System.out.println(scoreMap.get("张三")); // 输出:95

        // 方式1:遍历键值对效率最高,是最常用的方式。
        for (Map.Entry<String, Integer> entry : scoreMap.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }

        // 方式2:遍历所有键 通过keySet()获取所有键,再通过键获取值,适合只需操作键或单独获取值的场景。
        Iterator<String> iterator = scoreMap.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            System.out.println( key + ":" + scoreMap.get(key));
        }

        // 方式3:遍历值,适合只需要值、无需键的场景
        for (Integer value : scoreMap.values()) {
            System.out.println(value);
        }

        // 方式4:forEach 流式遍历(最简洁),JDK 8 及以上推荐使用。
        scoreMap.forEach((key, value) -> {
            System.out.println( key + ":" + value);
        });
    }
}

LinkedHashMap

  • 继承 HashMap,底层多了双向链表记录键值对插入顺序或访问顺序(可用于 LRU 缓存)。
import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        // 创建LinkedHashMap(默认按插入顺序存储)
        Map<String, String> map = new LinkedHashMap<>();

        // 添加键值对
        map.put("name", "张三");
        map.put("age", "20");
        map.put("gender", "男");
        map.put("score", "90");

        // 遍历(按插入顺序输出,与HashMap的无序不同)
        System.out.println("按插入顺序遍历:");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " → " + entry.getValue());
        }
        // 输出顺序:
        // name → 张三
        // age → 20
        // gender → 男
        // score → 90
    }
}

TreeMap

  • 底层基于红黑树,键自动排序(自然排序或自定义 Comparator),键不可为 null
import java.util.TreeMap;
import java.util.Map;

public class TreeMapDemo {
    public static void main(String[] args) {
        // 创建TreeMap(自动按键排序)
        Map<String, String> dict = new TreeMap<>();

        dict.put("banana", "香蕉");
        dict.put("apple", "苹果");
        dict.put("cat", "猫");

        // 遍历(按键的字母顺序排序)
        for (Map.Entry<String, String> entry : dict.entrySet()) {
            System.out.println(entry.getKey() + "→" + entry.getValue());
            // 输出:apple→苹果、banana→香蕉、cat→猫
        }
    }
}

Hashtable

  • 古老实现,线程安全(方法加 synchronized),效率低,键和值都不可为 null,已被 ConcurrentHashMap 替代。

ConcurrentHashMap

  • 线程安全的哈希表,JDK 1.8 采用 CAS + 同步锁(分段锁优化为节点锁),效率高于 Hashtable
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;

public class ConcurrentHashMapDemo {
    // 共享的ConcurrentHashMap
    private static final Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>();

    public static void main(String[] args) throws InterruptedException {
        // 线程1:添加元素
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                concurrentMap.put(i, i); // 并发添加,无需额外同步
            }
        });

        // 线程2:读取并修改元素
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                // 并发读取和修改,安全无异常
                concurrentMap.computeIfPresent(i, (k, v) -> v * 2); // 存在则翻倍
            }
        });

        // 启动线程
        thread1.start();
        thread2.start();

        // 等待线程结束
        thread1.join();
        thread2.join();

        // 验证结果(总数应为1000,且值正确)
        System.out.println("最终元素数量:" + concurrentMap.size()); // 1000
        System.out.println("键999的值:" + concurrentMap.get(999)); // 1998(999*2)
    }
}

四、集合工具类(java.util.Collections

提供静态方法操作集合,如:

  • 排序:sort(list)sort(list, comparator)
  • 线程安全包装:synchronizedList(list)synchronizedMap(map)
  • 不可变集合:unmodifiableList(list)emptyList()
  • 查找:binarySearch(list, key)(二分查找,需先排序)

五、选择建议

  • 查询多、增删少ArrayList
  • 增删多、查询少LinkedList
  • 去重且无序HashSet
  • 去重且排序TreeSet
  • 键值对且无序HashMap
  • 键值对且排序TreeMap
  • 线程安全ConcurrentHashMap(Map)、CopyOnWriteArrayList(List)

六、注意事项

  • 集合存储的是对象引用,基本类型需用包装类(如 IntegerBoolean)。
  • HashSet/HashMap 的元素需正确重写 hashCode() 和 equals(),否则可能导致重复元素。
  • 迭代器(Iterator)遍历集合时,若中途修改集合(如 add/remove),会抛出 ConcurrentModificationException(快速失败机制)。

掌握这些核心类的特性和适用场景,能高效处理 Java 中的数据存储与操作需求。