在 Java 编程中,集合框架(Collection Framework)是每个开发者必须掌握的核心内容。无论是处理用户输入、操作数据库结果,还是实现算法逻辑,集合类都扮演着至关重要的角色。

        今天,我们将从一个简单的字符串去重排序问题出发,深入探讨 Java 集合框架的常用类、核心方法以及最佳实践,帮助你构建完整的集合知识体系。


🎯 问题引入:字符串字符去重与升序输出

需求:输入字符串 banana,输出去重后的字符并按升序排列:

a
b
n

这个问题看似简单,却涉及了集合框架中多个重要概念。让我们以此为切入点,系统梳理 Java 集合体系。


📚 Java 集合框架整体架构

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

1. Collection 接口体系

  • List:有序、可重复
  • Set:无序(或有序)、不可重复
  • Queue:队列结构

2. Map 接口体系

  • 键值对存储,键不可重复

3. 工具类

  • Collections:集合操作工具
  • Arrays:数组操作工具
Collection
├── List
│   ├── ArrayList
│   ├── LinkedList
│   └── Vector
├── Set
│   ├── HashSet
│   ├── LinkedHashSet
│   └── TreeSet
└── Queue
    ├── PriorityQueue
    └── Deque (LinkedList 实现)

Map
├── HashMap
├── LinkedHashMap
├── TreeMap
└── Hashtable

📌 一、List 接口及其实现类

ArrayList vs LinkedList

特性 ArrayList LinkedList
底层结构 动态数组 双向链表
随机访问 O(1) ⭐ O(n)
插入/删除 O(n)(中间位置) O(1)(已知位置)⭐
内存占用 较少 较多(存储前后指针)
线程安全

常用方法

// 基础操作
list.add(E e);           // 添加元素
list.add(int index, E e); // 指定位置插入
list.get(int index);     // 获取指定位置元素
list.set(int index, E e); // 替换指定位置元素
list.remove(int index);  // 删除指定位置元素
list.remove(Object o);   // 删除第一个匹配元素
list.size();             // 获取大小
list.isEmpty();          // 是否为空

// List 特有方法
list.subList(int from, int to); // 获取子列表
Collections.sort(list);         // 排序
Collections.reverse(list);      // 反转

特殊方法(LinkedList)

// 作为 List 使用
linkedList.getFirst();    // 获取第一个元素
linkedList.getLast();     // 获取最后一个元素
linkedList.removeFirst(); // 删除并返回第一个元素
linkedList.removeLast();  // 删除并返回最后一个元素
linkedList.addFirst(E e); // 在开头添加
linkedList.addLast(E e);  // 在末尾添加

💡 回到最初的问题:如果题目要求"使用 LinkedList 的特殊方法将首尾元素分别替换为 start 和 end",正确做法是:

linkedList.removeFirst();
linkedList.addFirst("start");
linkedList.removeLast();
linkedList.addLast("end");

📌 二、Set 接口及其实现类

三种 Set 的对比

实现类 底层结构 是否有序 是否允许 null 时间复杂度
HashSet 哈希表 ❌ 无序 ✅ 允许一个 O(1)
LinkedHashSet 哈希表 + 链表 ✅ 插入顺序 ✅ 允许一个 O(1)
TreeSet 红黑树 ✅ 自然排序/自定义排序 ❌ 不允许 O(log n)

常用方法(所有 Set 共享)

set.add(E e);        // 添加元素(去重)
set.remove(Object o); // 删除元素
set.contains(Object o); // 是否包含
set.size();          // 大小
set.isEmpty();       // 是否为空
set.clear();         // 清空

// Set 特有操作
set1.addAll(set2);        // 并集
set1.retainAll(set2);     // 交集
set1.removeAll(set2);     // 差集

TreeSet 特有方法

treeSet.first();     // 返回第一个(最小)元素
treeSet.last();      // 返回最后一个(最大)元素
treeSet.lower(E e);  // 返回小于 e 的最大元素
treeSet.higher(E e); // 返回大于 e 的最小元素
treeSet.subSet(E from, E to); // 返回子集

解决字符串去重排序问题的最佳方案

TreeSet<Character> set = new TreeSet<>();
for (char c : input.toCharArray()) {
    set.add(c);
}

📌 三、Map 接口及其实现类

四种 Map 的对比

实现类 底层结构 是否有序 线程安全 null 支持
HashMap 哈希表 ❌ 无序 ✅ key/value 都支持
LinkedHashMap 哈希表 + 链表 ✅ 插入顺序/访问顺序
TreeMap 红黑树 ✅ key 自然排序 ❌ key 不支持
Hashtable 哈希表

常用方法

map.put(K key, V value);      // 添加/更新键值对
map.get(Object key);          // 获取值
map.remove(Object key);       // 删除键值对
map.containsKey(Object key);  // 是否包含键
map.containsValue(Object value); // 是否包含值
map.size();                   // 大小
map.isEmpty();                // 是否为空
map.clear();                  // 清空

// 遍历方式
for (Map.Entry<K, V> entry : map.entrySet()) {
    K key = entry.getKey();
    V value = entry.getValue();
}

// Java 8+ 新方法
map.forEach((k, v) -> System.out.println(k + "=" + v));
map.computeIfAbsent(key, k -> defaultValue); // 不存在时计算并添加
map.merge(key, value, (oldVal, newVal) -> oldVal + newVal); // 合并操作

📌 四、集合构造函数详解

集合到集合的转换

// List 构造函数
List<E> list = new ArrayList<>(collection);

// Set 构造函数  
Set<E> set = new HashSet<>(collection);
Set<E> sortedSet = new TreeSet<>(collection);

// Map 构造函数
Map<K, V> map = new HashMap<>(map2);
构造函数签名
// ArrayList
public ArrayList(Collection<? extends E> c)

// HashSet  
public HashSet(Collection<? extends E> c)

// TreeSet - 注意:要求元素可比较
public TreeSet(Collection<? extends E> c)

⚠️ 重要限制TreeSet 的构造函数要求传入的集合中所有元素都实现了 Comparable 接口,或者你在创建 TreeSet 时提供了 Comparator

泛型兼容性

✅ 正确示例:

Set<Character> charSet = new HashSet<>();
List<Character> charList = new ArrayList<>(charSet); // ✅ 完全兼容

Set<String> stringSet = new HashSet<>();
List<Object> objList = new ArrayList<>(stringSet);   // ✅ String 是 Object 的子类

❌ 错误示例:

Set<String> stringSet = new HashSet<>();
List<Integer> intList = new ArrayList<>(stringSet);  // ❌ 编译错误!

📌 五、Collections 工具类常用方法

Collections 类提供了大量静态方法来操作集合:

// 排序相关
Collections.sort(list);                    // 自然排序
Collections.sort(list, comparator);        // 自定义排序
Collections.reverse(list);                 // 反转

// 查找相关  
Collections.max(collection);               // 最大值
Collections.min(collection);               // 最小值
Collections.frequency(collection, obj);    // 元素出现次数

// 不可变集合
Collections.unmodifiableList(list);        // 返回不可修改的视图
Collections.emptyList();                   // 返回空列表

// 同步集合(线程安全)
Collections.synchronizedList(list);        // 返回线程安全的包装

// 其他实用方法
Collections.shuffle(list);                 // 随机打乱
Collections.fill(list, obj);               // 填充相同元素
Collections.replaceAll(list, oldVal, newVal); // 批量替换

🧪 实战案例:完整解决方案

结合我们学到的所有知识,来看几个完整的实战案例。

案例1:字符串字符去重排序(原问题)

import java.util.*;

public class StringProcessor {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String input = in.next();
        
        // 方案1:TreeSet(推荐)
        TreeSet<Character> sortedUniqueChars = new TreeSet<>();
        for (char c : input.toCharArray()) {
            sortedUniqueChars.add(c);
        }
        sortedUniqueChars.forEach(System.out::println);
        
        // 方案2:HashSet + 排序
        Set<Character> uniqueChars = new HashSet<>();
        for (char c : input.toCharArray()) {
            uniqueChars.add(c);
        }
        List<Character> sortedList = new ArrayList<>(uniqueChars);
        Collections.sort(sortedList);
        sortedList.forEach(System.out::println);
    }
}

案例2:统计字符频率

// 使用 Map 统计字符出现次数
Map<Character, Integer> charCount = new HashMap<>();
for (char c : input.toCharArray()) {
    charCount.merge(c, 1, Integer::sum);
}

// 按字符排序输出
new TreeMap<>(charCount).forEach((k, v) -> 
    System.out.println(k + ": " + v));

案例3:去重并保持插入顺序

// 使用 LinkedHashSet 保持插入顺序
Set<Character> orderedUnique = new LinkedHashSet<>();
for (char c : input.toCharArray()) {
    orderedUnique.add(c);
}
orderedUnique.forEach(System.out::println);
// 输入 "banana" 输出: b, a, n

🚀 性能建议与最佳实践

1. 选择合适的集合类型

  • 需要快速查找HashSet / HashMap
  • 需要排序TreeSet / TreeMap
  • 需要保持插入顺序LinkedHashSet / LinkedHashMap
  • 频繁在两端操作LinkedList
  • 随机访问为主ArrayList

2. 初始化容量

// 避免频繁扩容
List<String> list = new ArrayList<>(expectedSize);
Map<String, Integer> map = new HashMap<>(expectedSize);

3. 使用泛型

// ✅ 好的做法
List<String> list = new ArrayList<>();

// ❌ 避免原始类型
List list = new ArrayList();

4. 面向接口编程

// ✅ 推荐:依赖抽象
List<String> list = new ArrayList<>();

// ⚠️ 谨慎:依赖具体实现
ArrayList<String> list = new ArrayList<>();

5. 不可变集合(Java 9+)

// 创建不可变集合
List<String> immutableList = List.of("a", "b", "c");
Set<String> immutableSet = Set.of("x", "y", "z");
Map<String, Integer> immutableMap = Map.of("key", 1, "key2", 2);

🔚 总结

Java 集合框架是一个强大而灵活的工具箱,掌握其核心类和方法能够让你写出更高效、更优雅的代码。

场景 推荐集合 理由
去重 + 升序 TreeSet 自动排序,代码简洁
去重 + 插入顺序 LinkedHashSet 保持元素插入顺序
高频随机访问 ArrayList O(1) 访问性能
频繁头尾操作 LinkedList O(1) 头尾插入删除
键值对存储 HashMap 最佳平均性能
键值对排序 TreeMap 按键自动排序

通过本文的学习,你应该能够:

  • 理解各种集合类的特点和适用场景
  • 熟练使用集合的构造函数进行类型转换
  • 运用 Collections 工具类简化操作
  • 根据具体需求选择最合适的集合实现

集合框架是 Java 编程的基础,也是面试中的高频考点。希望这篇详细的解析能帮助你在实际开发中更加得心应手!

互动时间:你在项目中遇到过哪些集合相关的性能问题?欢迎在评论区分享你的经验和解决方案!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐