Java 集合详解

Java 集合框架(Collection Framework)详解

Java 集合框架是用于存储和操作一组对象的统一架构,位于 java.util 包下。它解决了数组的固定长度、类型单一等局限性,提供了灵活、高效的数据结构和操作方法。本文将从核心接口到常用实现类,结合代码示例详细讲解。

一、集合框架整体结构

集合框架的核心是 CollectionMap 两大接口,它们分别代表两种不同的数据结构:

  • Collection:存储单一元素的集合(如列表、集合),元素是独立的个体。
  • Map:存储键值对(Key-Value) 的集合(如字典),元素以 key:value 形式存在,key 唯一,value 可重复。

框架结构图(简化版):

java.util
├─ Collection(接口)  // 单元素集合根接口
│  ├─ List(接口)     // 有序、可重复
│  │  ├─ ArrayList(实现类)  // 动态数组
│  │  └─ LinkedList(实现类) // 双向链表
│  └─ Set(接口)      // 无序、不可重复
│     ├─ HashSet(实现类)   // 哈希表
│     └─ TreeSet(实现类)   // 红黑树(有序)
│
└─ Map(接口)         // 键值对集合根接口
   ├─ HashMap(实现类)    // 哈希表(key无序)
   ├─ TreeMap(实现类)    // 红黑树(key有序)
   └─ Hashtable(实现类)  // 哈希表(线程安全,过时)

二、核心接口:Collection

Collection 是所有单元素集合的根接口,定义了操作集合的通用方法(如添加、删除、遍历等)。其常用方法如下:

方法声明功能描述
boolean add(E e)添加元素到集合(成功返回true
boolean remove(Object o)从集合中删除指定元素
void clear()清空集合所有元素
boolean contains(Object o)判断集合是否包含指定元素
int size()返回集合元素个数
boolean isEmpty()判断集合是否为空
Iterator<E> iterator()返回迭代器(用于遍历集合)
1. List 接口(有序、可重复)

List 继承自 Collection,特点是:

  • 有序:元素按插入顺序排列,可通过索引(0 开始)访问。
  • 可重复:允许存储相同的元素(equals() 判定为 true)。

常用方法(除 Collection 通用方法外):

方法声明功能描述
E get(int index)获取指定索引的元素
E set(int index, E element)修改指定索引的元素(返回旧元素)
void add(int index, E element)在指定索引插入元素
E remove(int index)删除指定索引的元素(返回被删除元素)
int indexOf(Object o)返回元素首次出现的索引(未找到返回 -1
(1)ArrayList 实现类
  • 底层结构动态数组(容量可自动扩容,默认初始容量 10,满后扩容为原来的 1.5 倍)。
  • 特点
    • 查询快(通过索引直接访问,时间复杂度 O(1))。
    • 增删慢(需移动元素,尤其中间位置,时间复杂度 O(n))。
  • 适用场景:频繁查询、少量增删的场景(如数据展示列表)。

代码示例:ArrayList 基本操作

import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {
    public static void main(String[] args) {
        // 创建ArrayList(泛型指定元素类型为String)
        List<String> list = new ArrayList<>();

        // 1. 添加元素
        list.add("Java");    // 末尾添加
        list.add("Python");
        list.add(1, "C++");  // 指定索引插入(原索引1及之后元素后移)
        System.out.println("添加后:" + list); // [Java, C++, Python]

        // 2. 访问元素(通过索引)
        String element = list.get(0);
        System.out.println("索引0的元素:" + element); // Java

        // 3. 修改元素
        String old = list.set(2, "JavaScript"); // 修改索引2的元素
        System.out.println("修改后:" + list);   // [Java, C++, JavaScript]
        System.out.println("被替换的元素:" + old); // Python

        // 4. 删除元素
        String removed = list.remove(1); // 删除索引1的元素
        System.out.println("删除后:" + list); // [Java, JavaScript]
        System.out.println("被删除的元素:" + removed); // C++

        // 5. 遍历元素(3种方式)
        System.out.println("遍历方式1:普通for循环");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

        System.out.println("遍历方式2:增强for循环(foreach)");
        for (String s : list) {
            System.out.println(s);
        }

        System.out.println("遍历方式3:迭代器(Iterator)");
        java.util.Iterator<String> it = list.iterator();
        while (it.hasNext()) { // 判断是否有下一个元素
            String s = it.next(); // 获取下一个元素
            System.out.println(s);
        }
    }
}
(2)LinkedList 实现类
  • 底层结构双向链表(每个节点包含 prev(前节点)、next(后节点)指针)。
  • 特点
    • 增删快(只需修改指针,时间复杂度 O(1),尤其首尾位置)。
    • 查询慢(需从头/尾遍历,时间复杂度 O(n))。
  • 适用场景:频繁增删、少量查询的场景(如队列、栈)。

代码示例:LinkedList 特有操作

import java.util.LinkedList;

public class LinkedListDemo {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();

        // 添加元素(支持首尾快速操作)
        linkedList.add("A"); // 末尾添加
        linkedList.addFirst("B"); // 头部添加
        linkedList.addLast("C");  // 末尾添加
        System.out.println("链表内容:" + linkedList); // [B, A, C]

        // 特有方法:操作首尾元素
        System.out.println("首元素:" + linkedList.getFirst()); // B
        System.out.println("尾元素:" + linkedList.getLast());  // C

        // 移除首尾元素
        linkedList.removeFirst();
        linkedList.removeLast();
        System.out.println("移除首尾后:" + linkedList); // [A]
    }
}
2. Set 接口(无序、不可重复)

Set 继承自 Collection,特点是:

  • 无序:元素插入顺序不保证(HashSet),或按特定规则排序(TreeSet)。
  • 不可重复:不允许存储相同的元素(equals() 判定为 true 则视为重复)。

常用方法:与 Collection 一致(无索引相关方法,因为无序)。

(1)HashSet 实现类
  • 底层结构哈希表(数组 + 链表/红黑树),依赖元素的 hashCode()equals() 方法保证唯一性。
  • 去重原理
    1. 计算元素的 hashCode() 得到哈希值,确定在哈希表中的存储位置。
    2. 若该位置无元素,直接存入;若有元素,通过 equals() 比较,相同则不存,不同则以链表/红黑树形式存储(哈希冲突处理)。
  • 特点
    • 无序(存储顺序 ≠ 插入顺序)。
    • 查找、添加、删除效率高(平均时间复杂度 O(1))。
  • 注意:存储自定义对象时,必须重写 hashCode()equals() 方法,否则无法正确去重。

代码示例:HashSet 去重与自定义对象

import java.util.HashSet;
import java.util.Set;

// 自定义对象:学生(需重写hashCode和equals)
class Student {
    private String id;
    private String name;

    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // 重写equals:id相同则视为同一学生
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id.equals(student.id);
    }

    // 重写hashCode:与equals保持一致(id相同则hashCode相同)
    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public String toString() {
        return "Student{id='" + id + "', name='" + name + "'}";
    }
}

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> strSet = new HashSet<>();
        // 添加重复元素(自动去重)
        strSet.add("苹果");
        strSet.add("香蕉");
        strSet.add("苹果"); // 重复,不添加
        System.out.println("字符串集合:" + strSet); // [香蕉, 苹果](无序)

        // 存储自定义对象(需重写hashCode和equals)
        Set<Student> studentSet = new HashSet<>();
        studentSet.add(new Student("001", "张三"));
        studentSet.add(new Student("002", "李四"));
        studentSet.add(new Student("001", "张三三")); // id重复,视为同一对象,不添加
        System.out.println("学生集合:" + studentSet);
        // 输出:[Student{id='002', name='李四'}, Student{id='001', name='张三'}]
    }
}
(2)TreeSet 实现类
  • 底层结构红黑树(自平衡二叉查找树),元素会按自然顺序自定义比较器排序。
  • 特点
    • 有序(按排序规则排列,而非插入顺序)。
    • 查找、添加、删除时间复杂度 O(log n)
  • 排序方式
    1. 自然排序:元素实现 Comparable 接口,重写 compareTo() 方法(如 IntegerString 已实现)。
    2. 自定义排序:创建 TreeSet 时传入 Comparator 比较器。

代码示例:TreeSet 排序

import java.util.Comparator;
import java.util.TreeSet;

// 自定义对象:商品(实现Comparable接口,按价格自然排序)
class Product implements Comparable<Product> {
    private String name;
    private double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    // 自然排序:按价格升序(小→大)
    @Override
    public int compareTo(Product o) {
        // 价格相等时,按名称排序(避免价格相同的元素被去重)
        if (this.price == o.price) {
            return this.name.compareTo(o.name);
        }
        return Double.compare(this.price, o.price); // 等价于 (int)(this.price - o.price)
    }

    @Override
    public String toString() {
        return name + "(" + price + "元)";
    }
}

public class TreeSetDemo {
    public static void main(String[] args) {
        // 1. 自然排序(元素实现Comparable)
        TreeSet<Product> products = new TreeSet<>();
        products.add(new Product("手机", 3999));
        products.add(new Product("电脑", 5999));
        products.add(new Product("耳机", 999));
        System.out.println("按价格升序:" + products);
        // 输出:[耳机(999.0元), 手机(3999.0元), 电脑(5999.0元)]

        // 2. 自定义排序(传入Comparator,按价格降序)
        TreeSet<Product> descProducts = new TreeSet<>(
            new Comparator<Product>() {
                @Override
                public int compare(Product p1, Product p2) {
                    return Double.compare(p2.price, p1.price); // 反转比较顺序
                }
            }
        );
        descProducts.addAll(products);
        System.out.println("按价格降序:" + descProducts);
        // 输出:[电脑(5999.0元), 手机(3999.0元), 耳机(999.0元)]
    }
}

三、核心接口:Map

Map 是键值对集合的根接口,特点是:

  • 元素以 key:value 形式存在,key 唯一(重复添加会覆盖旧值),value 可重复。
  • keyvalue 均可为 nullHashMap 允许 keynull 一次,Hashtable 不允许)。

常用方法

方法声明功能描述
V put(K key, V value)添加键值对(返回被覆盖的旧值,无则返回null
V get(Object key)根据 key 获取 value(无则返回null
V remove(Object key)根据 key 删除键值对(返回被删除的value
boolean containsKey(Object key)判断是否包含指定 key
boolean containsValue(Object value)判断是否包含指定 value
Set<K> keySet()返回所有 keySet 集合
Collection<V> values()返回所有 valueCollection 集合
Set<Map.Entry<K,V>> entrySet()返回所有键值对(Entry对象)的 Set 集合
int size()返回键值对数量
void clear()清空所有键值对
1. HashMap 实现类
  • 底层结构哈希表(数组 + 链表/红黑树)key 的存储依赖 hashCode()equals()(同 HashSet)。
  • 特点
    • key 无序(存储顺序 ≠ 插入顺序)。
    • 查找、添加、删除效率高(平均 O(1))。
    • 非线程安全(多线程操作可能出问题)。
  • 适用场景:一般键值对存储,如缓存、配置映射等。

代码示例:HashMap 基本操作

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建HashMap(key为String,value为Integer)
        Map<String, Integer> map = new HashMap<>();

        // 1. 添加键值对
        map.put("Java", 90);
        map.put("Python", 85);
        map.put("Java", 95); // key重复,覆盖旧值(90→95)
        System.out.println("map内容:" + map); // {Python=85, Java=95}(无序)

        // 2. 获取value
        int javaScore = map.get("Java");
        System.out.println("Java分数:" + javaScore); // 95
        System.out.println("C++分数(不存在):" + map.get("C++")); // null

        // 3. 遍历方式(3种)
        System.out.println("方式1:遍历key,再获取value");
        Set<String> keys = map.keySet(); // 获取所有key
        for (String key : keys) {
            System.out.println(key + " → " + map.get(key));
        }

        System.out.println("方式2:直接遍历value(无法获取key)");
        for (Integer value : map.values()) {
            System.out.println("分数:" + value);
        }

        System.out.println("方式3:遍历键值对(Entry对象)");
        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry.getKey() + " → " + entry.getValue());
        }

        // 4. 删除键值对
        map.remove("Python");
        System.out.println("删除Python后:" + map); // {Java=95}
    }
}
2. TreeMap 实现类
  • 底层结构红黑树key 会按自然顺序自定义比较器排序。
  • 特点
    • key 有序(按排序规则排列)。
    • 查找、添加、删除时间复杂度 O(log n)
    • 非线程安全。
  • 排序方式:同 TreeSetkey 实现 Comparable 或传入 Comparator)。

代码示例:TreeMap 排序

import java.util.Comparator;
import java.util.TreeMap;

public class TreeMapDemo {
    public static void main(String[] args) {
        // 1. 自然排序(key为Integer,默认按数字升序)
        TreeMap<Integer, String> numMap = new TreeMap<>();
        numMap.put(3, "三");
        numMap.put(1, "一");
        numMap.put(2, "二");
        System.out.println("数字排序:" + numMap); // {1=一, 2=二, 3=三}

        // 2. 自定义排序(key为String,按长度降序)
        TreeMap<String, Integer> strMap = new TreeMap<>(
            new Comparator<String>() {
                @Override
                public int compare(String s1, String s2) {
                    // 按字符串长度降序,长度相同则按自然顺序
                    if (s1.length() != s2.length()) {
                        return Integer.compare(s2.length(), s1.length());
                    }
                    return s1.compareTo(s2);
                }
            }
        );
        strMap.put("apple", 5);
        strMap.put("banana", 6);
        strMap.put("cat", 3);
        System.out.println("按字符串长度降序:" + strMap);
        // 输出:{banana=6, apple=5, cat=3}(banana长度6最长,cat长度3最短)
    }
}
3. Hashtable 实现类(过时)
  • 底层结构:哈希表,与 HashMap 类似,但线程安全(方法加了 synchronized 锁)。
  • 缺点
    • 线程安全导致效率低,不适合高并发场景(推荐用 ConcurrentHashMap 替代)。
    • 不允许 keyvaluenull
  • 现状:JDK 1.0 推出,已被 HashMap 取代,仅兼容旧代码时使用。

四、集合框架的关键概念

1. 迭代器(Iterator

Iterator 是遍历集合的通用接口,适用于 Collection 体系(ListSet),提供统一的遍历方式:

  • boolean hasNext():判断是否有下一个元素。
  • E next():获取下一个元素(注意:必须先调用 hasNext(),否则可能抛 NoSuchElementException)。
  • void remove():删除当前遍历的元素(需在 next() 之后调用)。

示例

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

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 s = it.next();
            if (s.equals("B")) {
                it.remove(); // 删除当前元素(B)
            }
        }
        System.out.println(list); // [A, C]
    }
}
2. 泛型(Generic)

泛型用于限制集合中元素的类型,避免运行时类型转换错误(编译时检查):

  • 声明:List<String> list = new ArrayList<>();String 为泛型类型,限制只能存字符串)。
  • 好处:无需手动类型转换,编译时检查类型安全。

示例

// 无泛型:需强制转换,可能抛ClassCastException
List list = new ArrayList();
list.add("Java");
String s = (String) list.get(0); // 必须转换

// 有泛型:自动类型匹配,编译时检查
List<String> list2 = new ArrayList<>();
list2.add("Java");
String s2 = list2.get(0); // 无需转换
// list2.add(123); // 编译报错(不能添加整数)
3. 线程安全性
  • 非线程安全ArrayListLinkedListHashMapTreeMapHashSetTreeSet(多线程并发修改可能导致异常,如 ConcurrentModificationException)。
  • 线程安全VectorArrayList 的线程安全版,过时)、Hashtable(过时)、Collections.synchronizedList() 等工具类包装的集合,推荐使用 java.util.concurrent 包下的并发集合(如 ConcurrentHashMapCopyOnWriteArrayList)。

五、常用集合对比与选择

集合类型底层结构有序性重复性线程安全效率(增删查)适用场景
ArrayList动态数组插入顺序允许查快增删慢频繁查询、少量修改
LinkedList双向链表插入顺序允许增删快查慢频繁增删(如队列、栈)
HashSet哈希表无序不允许增删查快去重、无需排序
TreeSet红黑树排序后不允许增删查较快去重且需要排序
HashMap哈希表key无序key不允许增删查快一般键值对存储
TreeMap红黑树key排序key不允许增删查较快键值对需要按key排序
Hashtable哈希表key无序key不允许是(低效)增删查慢兼容旧代码(不推荐新用)

总结

Java 集合框架通过 Collection(单元素)和 Map(键值对)两大体系,提供了丰富的数据结构:

  • ListArrayListLinkedList)适合存储有序可重复的元素,重点关注查询或增删效率。
  • SetHashSetTreeSet)适合存储无序不可重复的元素,重点关注去重或排序需求。
  • MapHashMapTreeMap)适合存储键值对,重点关注 key 的唯一性和排序需求。

实际开发中,需根据有序性、重复性、操作效率、线程安全等需求选择合适的集合类,同时善用泛型和迭代器提高代码安全性和可读性。

如需巩固本文内容,可点击以下链接完成相关练习:点击此处进入练习题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值