Java 集合工具类详解
1. 集合工具类概述
Java 提供了丰富的集合工具类,主要包括 Collections 和 Arrays 工具类,以及 Apache Commons Collections 等第三方库。这些工具类提供了大量便捷的方法来操作集合,大大简化了集合操作的复杂度。
集合工具类 = 便捷操作 + 性能优化 + 线程安全 + 代码复用
- 🛠️ 便捷操作 - 提供丰富的方法简化常见集合操作,如排序、查找、转换等
- ⚡ 性能优化 - 经过优化的算法实现,提供高效的集合处理能力
- 🔒 线程安全 - 提供线程安全的集合包装和操作方法,简化并发编程
- 📦 代码复用 - 减少重复代码,提高开发效率和代码质量
- 🧩 标准化 - 提供统一的API接口,使代码更加一致和可维护
1.1 什么是集合工具类?
集合工具类是一组静态方法的集合,专门用于操作 Java 集合框架中的各种集合类型。它们提供了排序、查找、同步、不可变集合创建等常用功能,让开发者能够更高效地处理集合数据。
主要工具类分类
| 工具类 | 主要功能 | 适用场景 |
|---|---|---|
| Collections | 集合操作、排序、查找、同步 | List、Set、Map 等集合类型 |
| Arrays | 数组操作、排序、查找、转换 | 各种数组类型 |
| Apache Commons | 扩展功能、集合操作 | 复杂业务场景 |
| Stream API | 函数式操作、并行处理 | 数据流处理 |
1.2 工具类设计原则
1public class CollectionUtilsDesign {2 3 // 1. 静态方法设计4 public static <T> boolean isEmpty(Collection<T> collection) {5 return collection == null || collection.isEmpty();6 }7 8 // 2. 泛型支持9 public static <T> List<T> filter(Collection<T> collection, Predicate<T> predicate) {10 return collection.stream()11 .filter(predicate)12 .collect(Collectors.toList());13 }14 15 // 3. 空值安全16 public static <T> int safeSize(Collection<T> collection) {17 return collection == null ? 0 : collection.size();18 }19 20 // 4. 不可变集合21 public static <T> List<T> immutableList(Collection<T> collection) {22 return Collections.unmodifiableList(new ArrayList<>(collection));23 }24}- 静态方法:无需实例化,直接调用
- 泛型支持:类型安全,编译时检查
- 空值安全:避免空指针异常
- 不可变性:提供不可变集合,保证数据安全
2. Collections 工具类详解
Collections 工具类是 Java 集合框架中最重要的工具类,提供了丰富的静态方法来操作各种集合类型。
- 排序和查找操作
- 修改操作
- 集合包装
2.1 排序操作
Collections 工具类提供了多种排序方法,支持自然排序和自定义排序:
1public class CollectionsSortExample {2 public static void main(String[] args) {3 // 自然排序(要求元素实现Comparable接口)4 List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");5 Collections.sort(names);6 System.out.println("自然排序后: " + names);7 // 输出: [Alice, Bob, Charlie, David]8 9 // 自定义排序(使用Comparator)10 List<Person> people = Arrays.asList(11 new Person("Alice", 25),12 new Person("Bob", 30),13 new Person("Charlie", 20),14 new Person("David", 28)15 );16 17 // 按年龄排序18 Collections.sort(people, Comparator.comparing(Person::getAge));19 System.out.println("按年龄排序: " + people);20 21 // 按姓名排序22 Collections.sort(people, Comparator.comparing(Person::getName));23 System.out.println("按姓名排序: " + people);24 25 // 逆序排序26 Collections.sort(names, Collections.reverseOrder());27 System.out.println("逆序排序: " + names);28 // 输出: [David, Charlie, Bob, Alice]29 }30 31 static class Person {32 private String name;33 private int age;34 35 public Person(String name, int age) {36 this.name = name;37 this.age = age;38 }39 40 public String getName() { return name; }41 public int getAge() { return age; }42 43 @Override44 public String toString() {45 return name + "(" + age + ")";46 }47 }48}排序方法对比
| 方法 | 说明 | 时间复杂度 | 适用场景 |
|---|---|---|---|
Collections.sort(List) | 自然排序 | O(n log n) | 元素已实现Comparable |
Collections.sort(List, Comparator) | 自定义排序 | O(n log n) | 需要自定义排序逻辑 |
Collections.reverseOrder() | 逆序比较器 | - | 需要逆序排序 |
Collections.shuffle(List) | 随机打乱 | O(n) | 需要随机顺序 |
2.2 查找和替换操作
Collections 工具类提供了高效的查找和替换方法,特别是二分查找算法:
1public class CollectionsSearchExample {2 public static void main(String[] args) {3 // 二分查找(要求列表已排序)4 List<Integer> numbers = Arrays.asList(1, 3, 5, 7, 9, 11, 13, 15);5 System.out.println("有序列表: " + numbers);6 7 int target = 7;8 int index = Collections.binarySearch(numbers, target);9 if (index >= 0) {10 System.out.println(target + " 的索引: " + index);11 } else {12 System.out.println(target + " 未找到,应插入位置: " + (-index - 1));13 }14 15 // 查找最大最小值16 Integer max = Collections.max(numbers);17 Integer min = Collections.min(numbers);18 System.out.println("最大值: " + max + ", 最小值: " + min);19 20 // 替换所有元素21 List<String> list = new ArrayList<>(Arrays.asList("a", "b", "a", "c", "a"));22 System.out.println("替换前: " + list);23 Collections.replaceAll(list, "a", "x");24 System.out.println("替换后: " + list);25 26 // 填充集合27 List<String> fillList = new ArrayList<>(Arrays.asList("a", "b", "c"));28 System.out.println("填充前: " + fillList);29 Collections.fill(fillList, "default");30 System.out.println("填充后: " + fillList);31 32 // 查找子列表位置33 List<String> source = Arrays.asList("a", "b", "c", "d", "e", "f");34 List<String> target1 = Arrays.asList("c", "d");35 List<String> target2 = Arrays.asList("x", "y");36 37 int firstIndex = Collections.indexOfSubList(source, target1);38 int lastIndex = Collections.lastIndexOfSubList(source, target1);39 System.out.println("子列表 " + target1 + " 首次出现位置: " + firstIndex);40 System.out.println("子列表 " + target1 + " 最后出现位置: " + lastIndex);41 42 int notFound = Collections.indexOfSubList(source, target2);43 System.out.println("子列表 " + target2 + " 位置: " + notFound);44 }45}查找方法性能对比
| 方法 | 时间复杂度 | 前提条件 | 适用场景 |
|---|---|---|---|
Collections.binarySearch() | O(log n) | 列表已排序 | 有序列表快速查找 |
Collections.max()/min() | O(n) | 无 | 查找最大最小值 |
Collections.indexOfSubList() | O(n*m) | 无 | 查找子列表位置 |
Collections.frequency() | O(n) | 无 | 统计元素出现次数 |
2.3 集合修改操作
Collections 工具类提供了丰富的集合修改方法,包括元素交换、旋转、打乱、反转等:
1public class CollectionsModifyExample {2 public static void main(String[] args) {3 // 交换元素4 List<String> swapList = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));5 System.out.println("交换前: " + swapList);6 Collections.swap(swapList, 0, 2);7 System.out.println("交换后: " + swapList);8 9 // 旋转集合10 List<String> rotateList = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e"));11 System.out.println("旋转前: " + rotateList);12 13 // 向右旋转2位14 Collections.rotate(rotateList, 2);15 System.out.println("向右旋转2位: " + rotateList);16 17 // 向左旋转1位18 Collections.rotate(rotateList, -1);19 System.out.println("向左旋转1位: " + rotateList);20 21 // 反转列表22 List<Integer> reverseList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));23 System.out.println("反转前: " + reverseList);24 Collections.reverse(reverseList);25 System.out.println("反转后: " + reverseList);26 27 // 复制集合28 List<String> source = Arrays.asList("a", "b", "c");29 List<String> dest = Arrays.asList("x", "y", "z");30 System.out.println("复制前: 源=" + source + ", 目标=" + dest);31 Collections.copy(dest, source);32 System.out.println("复制后: 目标=" + dest);33 34 // 替换所有元素35 List<String> replaceList = new ArrayList<>(Arrays.asList("a", "b", "a", "c", "a"));36 System.out.println("替换前: " + replaceList);37 Collections.replaceAll(replaceList, "a", "x");38 System.out.println("替换后: " + replaceList);39 }40}| 方法 | 功能 | 时间复杂度 | 特点 |
|---|---|---|---|
swap(List, int, int) | 交换两个位置的元素 | O(1) | 原地修改 |
rotate(List, int) | 将列表旋转指定距离 | O(n) | 正数右移,负数左移 |
reverse(List) | 反转列表 | O(n) | 原地修改 |
shuffle(List) | 随机打乱列表 | O(n) | 使用默认随机源 |
copy(List dest, List src) | 复制列表 | O(n) | 目标必须足够大 |
fill(List, Object) | 用指定元素填充列表 | O(n) | 覆盖所有元素 |
replaceAll(List, old, new) | 替换所有指定元素 | O(n) | 替换全部匹配项 |
2.3 同步和不可变集合
Collections 工具类提供了创建线程安全集合和不可变集合的方法,确保在多线程环境下的数据安全:
1public class CollectionsThreadSafetyExample {2 public static void main(String[] args) {3 // 创建同步集合(线程安全)4 List<String> syncList = Collections.synchronizedList(new ArrayList<>());5 Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());6 Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());7 8 // 多线程安全操作9 Runnable addTask = () -> {10 for (int i = 0; i < 100; i++) {11 syncList.add("Thread-" + Thread.currentThread().getId() + "-" + i);12 }13 };14 15 Thread thread1 = new Thread(addTask);16 Thread thread2 = new Thread(addTask);17 18 thread1.start();19 thread2.start();20 21 try {22 thread1.join();23 thread2.join();24 } catch (InterruptedException e) {25 e.printStackTrace();26 }27 28 System.out.println("同步集合最终大小: " + syncList.size());29 30 // 创建不可变集合31 List<String> immutableList = Collections.unmodifiableList(Arrays.asList("a", "b", "c"));32 Set<String> immutableSet = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("x", "y", "z")));33 Map<String, String> immutableMap = Collections.unmodifiableMap(Map.of("key1", "value1", "key2", "value2"));34 35 System.out.println("不可变集合: " + immutableList);36 System.out.println("不可变Set: " + immutableSet);37 System.out.println("不可变Map: " + immutableMap);38 39 // 尝试修改不可变集合会抛出异常40 try {41 immutableList.add("d"); // 抛出UnsupportedOperationException42 } catch (UnsupportedOperationException e) {43 System.out.println("不能修改不可变集合: " + e.getMessage());44 }45 46 // 创建单元素集合47 List<String> singletonList = Collections.singletonList("single");48 Set<String> singletonSet = Collections.singleton("single");49 Map<String, String> singletonMap = Collections.singletonMap("key", "value");50 51 System.out.println("单元素List: " + singletonList);52 System.out.println("单元素Set: " + singletonSet);53 System.out.println("单元素Map: " + singletonMap);54 }55}线程安全集合对比
| 类型 | 同步方式 | 性能影响 | 适用场景 |
|---|---|---|---|
Collections.synchronizedXXX() | 方法级同步 | 中等 | 简单同步需求 |
ConcurrentHashMap | 分段锁 | 低 | 高并发读多写少 |
CopyOnWriteArrayList | 写时复制 | 高 | 读多写少场景 |
Collections.unmodifiableXXX() | 不可变 | 无 | 只读数据 |
2.4 集合操作
Collections 工具类提供了丰富的集合操作方法,包括元素交换、旋转、打乱、反转等:
1public class CollectionsOperationsExample {2 public static void main(String[] args) {3 // 交换元素4 List<String> swapList = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));5 System.out.println("交换前: " + swapList);6 Collections.swap(swapList, 0, 2);7 System.out.println("交换后: " + swapList);8 9 // 旋转集合10 List<String> rotateList = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e"));11 System.out.println("旋转前: " + rotateList);12 13 // 向右旋转2位14 Collections.rotate(rotateList, 2);15 System.out.println("向右旋转2位: " + rotateList);16 17 // 向左旋转1位18 Collections.rotate(rotateList, -1);19 System.out.println("向左旋转1位: " + rotateList);20 21 // 随机打乱22 List<String> shuffleList = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e"));23 System.out.println("打乱前: " + shuffleList);24 25 // 使用固定种子确保可重复结果26 Collections.shuffle(shuffleList, new Random(42));27 System.out.println("打乱后: " + shuffleList);28 29 // 反转集合30 List<String> reverseList = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));31 System.out.println("反转前: " + reverseList);32 Collections.reverse(reverseList);33 System.out.println("反转后: " + reverseList);34 35 // 集合操作组合使用36 List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8));37 System.out.println("原始集合: " + numbers);38 39 // 先排序,再反转,最后随机打乱40 Collections.sort(numbers);41 System.out.println("排序后: " + numbers);42 43 Collections.reverse(numbers);44 System.out.println("反转后: " + numbers);45 46 Collections.shuffle(numbers);47 System.out.println("打乱后: " + numbers);48 }49}集合操作方法对比
| 方法 | 功能 | 时间复杂度 | 适用场景 |
|---|---|---|---|
Collections.swap() | 交换两个元素 | O(1) | 需要交换特定位置元素 |
Collections.rotate() | 旋转集合 | O(n) | 循环移位操作 |
Collections.shuffle() | 随机打乱 | O(n) | 需要随机顺序 |
Collections.reverse() | 反转集合 | O(n) | 需要逆序排列 |
2.5 频率统计和集合关系
Collections 工具类提供了统计元素频率和检查集合关系的方法:
1public class CollectionsStatisticsExample {2 public static void main(String[] args) {3 // 统计元素出现次数4 List<String> freqList = Arrays.asList("a", "b", "a", "c", "a", "b", "d", "a");5 System.out.println("原始列表: " + freqList);6 7 int aCount = Collections.frequency(freqList, "a");8 int bCount = Collections.frequency(freqList, "b");9 int cCount = Collections.frequency(freqList, "c");10 int dCount = Collections.frequency(freqList, "d");11 int xCount = Collections.frequency(freqList, "x");12 13 System.out.println("a出现次数: " + aCount);14 System.out.println("b出现次数: " + bCount);15 System.out.println("c出现次数: " + cCount);16 System.out.println("d出现次数: " + dCount);17 System.out.println("x出现次数: " + xCount);18 19 // 检查两个集合是否有共同元素20 List<String> list1 = Arrays.asList("a", "b", "c", "d");21 List<String> list2 = Arrays.asList("b", "c", "d", "e");22 List<String> list3 = Arrays.asList("x", "y", "z");23 24 boolean hasCommon1 = !Collections.disjoint(list1, list2);25 boolean hasCommon2 = !Collections.disjoint(list1, list3);26 27 System.out.println("list1和list2有共同元素: " + hasCommon1);28 System.out.println("list1和list3有共同元素: " + hasCommon2);29 30 // 统计所有元素的频率31 Map<String, Long> frequencyMap = freqList.stream()32 .collect(Collectors.groupingBy(33 Function.identity(),34 Collectors.counting()35 ));36 37 System.out.println("元素频率统计: " + frequencyMap);38 39 // 找出出现次数最多的元素40 String mostFrequent = frequencyMap.entrySet().stream()41 .max(Map.Entry.comparingByValue())42 .map(Map.Entry::getKey)43 .orElse("无");44 45 System.out.println("出现次数最多的元素: " + mostFrequent);46 47 // 检查集合包含关系48 List<String> subset = Arrays.asList("a", "b");49 boolean containsAll = freqList.containsAll(subset);50 System.out.println("freqList包含subset的所有元素: " + containsAll);51 }52}统计方法功能对比
| 方法 | 功能 | 返回值 | 适用场景 |
|---|---|---|---|
Collections.frequency() | 统计元素出现次数 | int | 需要知道特定元素出现次数 |
Collections.disjoint() | 检查集合是否无交集 | boolean | 检查两个集合是否完全独立 |
Collection.containsAll() | 检查包含关系 | boolean | 检查一个集合是否包含另一个集合的所有元素 |
3. Arrays 工具类详解
Arrays 工具类是 Java 中专门用于操作数组的工具类,提供了排序、查找、比较、填充等常用功能。
3.1 数组排序和查找
Arrays 工具类提供了高效的数组排序和查找方法:
1public class ArraysSortExample {2 public static void main(String[] args) {3 // 基本类型数组排序4 int[] numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};5 System.out.println("原始数组: " + Arrays.toString(numbers));6 7 // 完整排序8 Arrays.sort(numbers);9 System.out.println("排序后: " + Arrays.toString(numbers));10 11 // 部分排序(只排序索引2到7的元素)12 int[] partialSort = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};13 System.out.println("部分排序前: " + Arrays.toString(partialSort));14 Arrays.sort(partialSort, 2, 8);15 System.out.println("部分排序后: " + Arrays.toString(partialSort));16 17 // 二分查找(要求数组已排序)18 int target = 5;19 int index = Arrays.binarySearch(numbers, target);20 if (index >= 0) {21 System.out.println(target + " 的索引: " + index);22 } else {23 System.out.println(target + " 未找到,应插入位置: " + (-index - 1));24 }25 26 // 查找所有匹配的元素27 List<Integer> indices = new ArrayList<>();28 for (int i = 0; i < numbers.length; i++) {29 if (numbers[i] == target) {30 indices.add(i);31 }32 }33 System.out.println(target + " 的所有索引: " + indices);34 35 // 对象数组排序36 String[] names = {"Charlie", "Alice", "Bob", "David", "Eve"};37 System.out.println("原始字符串数组: " + Arrays.toString(names));38 39 // 自然排序40 Arrays.sort(names);41 System.out.println("自然排序后: " + Arrays.toString(names));42 43 // 自定义排序(按长度)44 Arrays.sort(names, Comparator.comparing(String::length));45 System.out.println("按长度排序后: " + Arrays.toString(names));46 47 // 并行排序(大数据量)48 int[] largeArray = new int[100000];49 Random random = new Random(42);50 for (int i = 0; i < largeArray.length; i++) {51 largeArray[i] = random.nextInt(10000);52 }53 54 long startTime = System.currentTimeMillis();55 Arrays.sort(largeArray);56 long endTime = System.currentTimeMillis();57 System.out.println("普通排序耗时: " + (endTime - startTime) + "ms");58 59 // 重置数组60 for (int i = 0; i < largeArray.length; i++) {61 largeArray[i] = random.nextInt(10000);62 }63 64 startTime = System.currentTimeMillis();65 Arrays.parallelSort(largeArray);66 endTime = System.currentTimeMillis();67 System.out.println("并行排序耗时: " + (endTime - startTime) + "ms");68 }69}排序方法性能对比
| 方法 | 适用类型 | 时间复杂度 | 适用场景 |
|---|---|---|---|
Arrays.sort() | 基本类型、对象数组 | O(n log n) | 一般排序需求 |
Arrays.parallelSort() | 基本类型、对象数组 | O(n log n) | 大数据量排序 |
Arrays.sort(array, from, to) | 基本类型、对象数组 | O(n log n) | 部分排序 |
Arrays.sort(array, comparator) | 对象数组 | O(n log n) | 自定义排序 |
3.2 数组比较和填充
Arrays 工具类提供了数组比较和填充的便捷方法:
1public class ArraysComparisonExample {2 public static void main(String[] args) {3 // 一维数组比较4 int[] arr1 = {1, 2, 3, 4, 5};5 int[] arr2 = {1, 2, 3, 4, 5};6 int[] arr3 = {1, 2, 3, 4, 6};7 8 System.out.println("arr1: " + Arrays.toString(arr1));9 System.out.println("arr2: " + Arrays.toString(arr2));10 System.out.println("arr3: " + Arrays.toString(arr3));11 12 boolean equals1 = Arrays.equals(arr1, arr2);13 boolean equals2 = Arrays.equals(arr1, arr3);14 15 System.out.println("arr1 equals arr2: " + equals1);16 System.out.println("arr1 equals arr3: " + equals2);17 18 // 多维数组深度比较19 int[][] matrix1 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};20 int[][] matrix2 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};21 int[][] matrix3 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 0}};22 23 System.out.println("matrix1: " + Arrays.deepToString(matrix1));24 System.out.println("matrix2: " + Arrays.deepToString(matrix2));25 System.out.println("matrix3: " + Arrays.deepToString(matrix3));26 27 boolean deepEquals1 = Arrays.deepEquals(matrix1, matrix2);28 boolean deepEquals2 = Arrays.deepEquals(matrix1, matrix3);29 30 System.out.println("matrix1 deepEquals matrix2: " + deepEquals1);31 System.out.println("matrix1 deepEquals matrix3: " + deepEquals2);32 33 // 数组填充34 int[] fillArray = new int[8];35 System.out.println("填充前: " + Arrays.toString(fillArray));36 37 // 全部填充38 Arrays.fill(fillArray, 42);39 System.out.println("全部填充42后: " + Arrays.toString(fillArray));40 41 // 部分填充42 Arrays.fill(fillArray, 2, 6, 100);43 System.out.println("索引2-5填充100后: " + Arrays.toString(fillArray));44 45 // 重置数组46 Arrays.fill(fillArray, 0);47 System.out.println("重置为0后: " + Arrays.toString(fillArray));48 49 // 对象数组填充50 String[] stringArray = new String[5];51 System.out.println("字符串数组填充前: " + Arrays.toString(stringArray));52 53 Arrays.fill(stringArray, "default");54 System.out.println("填充default后: " + Arrays.toString(stringArray));55 56 // 部分填充字符串57 Arrays.fill(stringArray, 1, 4, "custom");58 System.out.println("索引1-3填充custom后: " + Arrays.toString(stringArray));59 60 // 数组复制和比较61 int[] original = {1, 2, 3, 4, 5};62 int[] copy1 = Arrays.copyOf(original, 3); // 复制前3个元素63 int[] copy2 = Arrays.copyOf(original, 7); // 复制并扩展长度64 int[] copy3 = Arrays.copyOfRange(original, 1, 4); // 复制指定范围65 66 System.out.println("原始数组: " + Arrays.toString(original));67 System.out.println("复制前3个: " + Arrays.toString(copy1));68 System.out.println("复制并扩展: " + Arrays.toString(copy2));69 System.out.println("复制范围1-3: " + Arrays.toString(copy3));70 71 // 比较不同长度的数组72 boolean equalsLength = Arrays.equals(original, copy1);73 System.out.println("original equals copy1: " + equalsLength);74 }75}比较和填充方法对比
| 方法 | 功能 | 适用场景 | 注意事项 |
|---|---|---|---|
Arrays.equals() | 一维数组比较 | 简单数组比较 | 只比较引用和内容,不比较长度 |
Arrays.deepEquals() | 多维数组比较 | 嵌套数组比较 | 递归比较所有层级 |
Arrays.fill() | 数组填充 | 初始化或重置数组 | 支持部分填充 |
Arrays.copyOf() | 数组复制 | 创建数组副本 | 可以改变数组长度 |
3.3 数组转换
Arrays 工具类提供了丰富的数组转换方法,支持数组与集合、流之间的转换:
1public class ArraysConversionExample {2 public static void main(String[] args) {3 // 基本类型数组转List4 int[] intArray = {1, 2, 3, 4, 5};5 System.out.println("原始int数组: " + Arrays.toString(intArray));6 7 // 基本类型数组转Stream8 IntStream intStream = Arrays.stream(intArray);9 List<Integer> intList = intStream.boxed().collect(Collectors.toList());10 System.out.println("转换为List: " + intList);11 12 // 对象数组转List13 String[] stringArray = {"Alice", "Bob", "Charlie", "David"};14 System.out.println("原始字符串数组: " + Arrays.toString(stringArray));15 16 // 使用Arrays.asList转换17 List<String> stringList = Arrays.asList(stringArray);18 System.out.println("Arrays.asList结果: " + stringList);19 20 // 注意:Arrays.asList返回的List是固定大小的21 try {22 stringList.add("Eve"); // 抛出UnsupportedOperationException23 } catch (UnsupportedOperationException e) {24 System.out.println("不能修改固定大小的List: " + e.getMessage());25 }26 27 // 创建可修改的List28 List<String> modifiableList = new ArrayList<>(Arrays.asList(stringArray));29 modifiableList.add("Eve");30 System.out.println("可修改的List: " + modifiableList);31 32 // 使用Stream API转换33 List<String> streamList = Arrays.stream(stringArray)34 .filter(s -> s.length() > 4)35 .map(String::toUpperCase)36 .collect(Collectors.toList());37 System.out.println("Stream转换结果: " + streamList);38 39 // 多维数组转换40 int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};41 System.out.println("原始矩阵: " + Arrays.deepToString(matrix));42 43 // 多维数组转Stream44 List<Integer> flatList = Arrays.stream(matrix)45 .flatMapToInt(Arrays::stream)46 .boxed()47 .collect(Collectors.toList());48 System.out.println("展平后的List: " + flatList);49 50 // 数组转Set(去重)51 int[] duplicateArray = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};52 Set<Integer> uniqueSet = Arrays.stream(duplicateArray)53 .boxed()54 .collect(Collectors.toSet());55 System.out.println("去重后的Set: " + uniqueSet);56 57 // 数组转Map58 String[] keys = {"name", "age", "city"};59 Object[] values = {"Alice", 25, "New York"};60 61 Map<String, Object> map = new HashMap<>();62 for (int i = 0; i < keys.length; i++) {63 map.put(keys[i], values[i]);64 }65 System.out.println("转换后的Map: " + map);66 67 // 使用Stream API创建Map68 Map<String, Object> streamMap = IntStream.range(0, keys.length)69 .boxed()70 .collect(Collectors.toMap(71 i -> keys[i],72 i -> values[i]73 ));74 System.out.println("Stream创建的Map: " + streamMap);75 }76}转换方法对比
| 方法 | 功能 | 返回值类型 | 注意事项 |
|---|---|---|---|
Arrays.asList() | 数组转List | 固定大小List | 不能添加/删除元素 |
Arrays.stream() | 数组转Stream | Stream | 支持函数式操作 |
new ArrayList<>(Arrays.asList()) | 数组转可变List | 可变List | 可以修改 |
Arrays.stream().boxed() | 基本类型转包装类型 | Stream<包装类型> | 自动装箱 |
4. 实用工具方法
在实际开发中,我们经常需要创建一些通用的工具方法来简化集合操作。以下是一些常用的实用工具方法:
4.1 集合判空和null检查
1public class CollectionUtils {2 3 /**4 * 检查集合是否为空5 * @param collection 要检查的集合6 * @return true如果集合为null或空,否则false7 */8 public static boolean isEmpty(Collection<?> collection) {9 return collection == null || collection.isEmpty();10 }11 12 /**13 * 检查集合是否不为空14 * @param collection 要检查的集合15 * @return true如果集合不为null且不为空,否则false16 */17 public static boolean isNotEmpty(Collection<?> collection) {18 return !isEmpty(collection);19 }20 21 /**22 * 安全获取集合大小23 * @param collection 要检查的集合24 * @return 集合大小,如果为null则返回025 */26 public static int size(Collection<?> collection) {27 return collection == null ? 0 : collection.size();28 }29 30 /**31 * 检查Map是否为空32 * @param map 要检查的Map33 * @return true如果Map为null或空,否则false34 */35 public static boolean isEmpty(Map<?, ?> map) {36 return map == null || map.isEmpty();37 }38 39 /**40 * 检查Map是否不为空41 * @param map 要检查的Map42 * @return true如果Map不为null且不为空,否则false43 */44 public static boolean isNotEmpty(Map<?, ?> map) {45 return !isEmpty(map);46 }47 48 /**49 * 创建空集合50 * @return 不可变的空List51 */52 public static <T> List<T> emptyList() {53 return Collections.emptyList();54 }55 56 /**57 * 创建空Set58 * @return 不可变的空Set59 */60 public static <T> Set<T> emptySet() {61 return Collections.emptySet();62 }63 64 /**65 * 创建空Map66 * @return 不可变的空Map67 */68 public static <K, V> Map<K, V> emptyMap() {69 return Collections.emptyMap();70 }71 72 /**73 * 检查数组是否为空74 * @param array 要检查的数组75 * @return true如果数组为null或长度为0,否则false76 */77 public static boolean isEmpty(Object[] array) {78 return array == null || array.length == 0;79 }80 81 /**82 * 检查数组是否不为空83 * @param array 要检查的数组84 * @return true如果数组不为null且长度大于0,否则false85 */86 public static boolean isNotEmpty(Object[] array) {87 return !isEmpty(array);88 }89}判空方法对比
| 方法 | 功能 | 返回值 | 适用场景 |
|---|---|---|---|
isEmpty() | 检查集合/Map/数组是否为空 | boolean | 需要判断集合状态 |
isNotEmpty() | 检查集合/Map/数组是否不为空 | boolean | 需要判断集合状态 |
size() | 安全获取集合大小 | int | 需要获取集合大小 |
emptyXXX() | 创建空集合 | 不可变集合 | 作为默认返回值 |
4.2 集合转换工具
集合转换工具类提供了各种集合类型之间的转换方法:
1public class CollectionConverter {2 3 /**4 * List转Set(去重)5 * @param list 要转换的List6 * @return 包含List元素的Set7 */8 public static <T> Set<T> listToSet(List<T> list) {9 if (isEmpty(list)) {10 return new HashSet<>();11 }12 return new HashSet<>(list);13 }14 15 /**16 * Set转List17 * @param set 要转换的Set18 * @return 包含Set元素的List19 */20 public static <T> List<T> setToList(Set<T> set) {21 if (isEmpty(set)) {22 return new ArrayList<>();23 }24 return new ArrayList<>(set);25 }26 27 /**28 * 数组转List29 * @param array 要转换的数组30 * @return 包含数组元素的List31 */32 public static <T> List<T> arrayToList(T[] array) {33 if (isEmpty(array)) {34 return new ArrayList<>();35 }36 return Arrays.asList(array);37 }38 39 /**40 * 数组转可变List41 * @param array 要转换的数组42 * @return 可修改的List43 */44 public static <T> List<T> arrayToModifiableList(T[] array) {45 if (isEmpty(array)) {46 return new ArrayList<>();47 }48 return new ArrayList<>(Arrays.asList(array));49 }50 51 /**52 * List转数组53 * @param list 要转换的List54 * @param clazz 目标数组的类型55 * @return 包含List元素的数组56 */57 public static <T> T[] listToArray(List<T> list, Class<T> clazz) {58 if (isEmpty(list)) {59 @SuppressWarnings("unchecked")60 T[] array = (T[]) Array.newInstance(clazz, 0);61 return array;62 }63 64 @SuppressWarnings("unchecked")65 T[] array = (T[]) Array.newInstance(clazz, list.size());66 return list.toArray(array);67 }68 69 /**70 * 基本类型数组转包装类型List71 * @param array 基本类型数组72 * @return 包装类型List73 */74 public static List<Integer> intArrayToList(int[] array) {75 if (array == null || array.length == 0) {76 return new ArrayList<>();77 }78 return Arrays.stream(array).boxed().collect(Collectors.toList());79 }80 81 /**82 * 基本类型数组转包装类型Set83 * @param array 基本类型数组84 * @return 包装类型Set85 */86 public static Set<Integer> intArrayToSet(int[] array) {87 if (array == null || array.length == 0) {88 return new HashSet<>();89 }90 return Arrays.stream(array).boxed().collect(Collectors.toSet());91 }92 93 /**94 * 字符串数组转List95 * @param array 字符串数组96 * @return 字符串List97 */98 public static List<String> stringArrayToList(String[] array) {99 if (isEmpty(array)) {100 return new ArrayList<>();101 }102 return Arrays.asList(array);103 }104 105 /**106 * 字符串数组转Set(去重)107 * @param array 字符串数组108 * @return 字符串Set109 */110 public static Set<String> stringArrayToSet(String[] array) {111 if (isEmpty(array)) {112 return new HashSet<>();113 }114 return new HashSet<>(Arrays.asList(array));115 }116 117 /**118 * 集合转字符串119 * @param collection 要转换的集合120 * @param delimiter 分隔符121 * @return 用分隔符连接的字符串122 */123 public static String collectionToString(Collection<?> collection, String delimiter) {124 if (isEmpty(collection)) {125 return "";126 }127 return collection.stream()128 .map(Object::toString)129 .collect(Collectors.joining(delimiter));130 }131 132 /**133 * 字符串转List134 * @param str 要转换的字符串135 * @param delimiter 分隔符136 * @return 分割后的字符串List137 */138 public static List<String> stringToList(String str, String delimiter) {139 if (str == null || str.trim().isEmpty()) {140 return new ArrayList<>();141 }142 return Arrays.asList(str.split(delimiter));143 }144}转换方法功能对比
| 方法 | 功能 | 输入类型 | 输出类型 | 特点 |
|---|---|---|---|---|
listToSet() | List转Set | List<T> | Set<T> | 自动去重 |
setToList() | Set转List | Set<T> | List<T> | 保持顺序 |
arrayToList() | 数组转List | T[] | List<T> | 固定大小 |
arrayToModifiableList() | 数组转可变List | T[] | List<T> | 可修改 |
listToArray() | List转数组 | List<T> | T[] | 类型安全 |
4.3 集合操作工具
集合操作工具类提供了集合之间的数学运算,如交集、并集、差集等:
1public class CollectionOperations {2 3 /**4 * 计算两个集合的交集5 * @param c1 第一个集合6 * @param c2 第二个集合7 * @return 包含共同元素的Set8 */9 public static <T> Set<T> intersection(Collection<T> c1, Collection<T> c2) {10 if (isEmpty(c1) || isEmpty(c2)) {11 return new HashSet<>();12 }13 Set<T> result = new HashSet<>(c1);14 result.retainAll(c2);15 return result;16 }17 18 /**19 * 计算两个集合的并集20 * @param c1 第一个集合21 * @param c2 第二个集合22 * @return 包含所有元素的Set23 */24 public static <T> Set<T> union(Collection<T> c1, Collection<T> c2) {25 Set<T> result = new HashSet<>();26 if (isNotEmpty(c1)) {27 result.addAll(c1);28 }29 if (isNotEmpty(c2)) {30 result.addAll(c2);31 }32 return result;33 }34 35 /**36 * 计算两个集合的差集(c1 - c2)37 * @param c1 第一个集合38 * @param c2 第二个集合39 * @return 包含c1中但不在c2中的元素40 */41 public static <T> Set<T> difference(Collection<T> c1, Collection<T> c2) {42 if (isEmpty(c1)) {43 return new HashSet<>();44 }45 if (isEmpty(c2)) {46 return new HashSet<>(c1);47 }48 Set<T> result = new HashSet<>(c1);49 result.removeAll(c2);50 return result;51 }52 53 /**54 * 计算两个集合的对称差集(并集 - 交集)55 * @param c1 第一个集合56 * @param c2 第二个集合57 * @return 包含只在一个集合中出现的元素58 */59 public static <T> Set<T> symmetricDifference(Collection<T> c1, Collection<T> c2) {60 Set<T> union = union(c1, c2);61 Set<T> intersection = intersection(c1, c2);62 union.removeAll(intersection);63 return union;64 }65 66 /**67 * 分割集合为指定大小的子集合68 * @param list 要分割的List69 * @param size 每个子集合的大小70 * @return 分割后的子集合List71 */72 public static <T> List<List<T>> partition(List<T> list, int size) {73 if (isEmpty(list) || size <= 0) {74 return new ArrayList<>();75 }76 77 List<List<T>> partitions = new ArrayList<>();78 for (int i = 0; i < list.size(); i += size) {79 int end = Math.min(i + size, list.size());80 partitions.add(list.subList(i, end));81 }82 return partitions;83 }84 85 /**86 * 检查一个集合是否是另一个集合的子集87 * @param subset 可能的子集88 * @param superset 可能的超集89 * @return true如果subset是superset的子集90 */91 public static <T> boolean isSubset(Collection<T> subset, Collection<T> superset) {92 if (isEmpty(subset)) {93 return true; // 空集合是任何集合的子集94 }95 if (isEmpty(superset)) {96 return false;97 }98 return superset.containsAll(subset);99 }100 101 /**102 * 检查两个集合是否相等(忽略顺序)103 * @param c1 第一个集合104 * @param c2 第二个集合105 * @return true如果两个集合包含相同的元素106 */107 public static <T> boolean equalsIgnoreOrder(Collection<T> c1, Collection<T> c2) {108 if (c1 == c2) {109 return true;110 }111 if (isEmpty(c1) && isEmpty(c2)) {112 return true;113 }114 if (isEmpty(c1) || isEmpty(c2)) {115 return false;116 }117 if (c1.size() != c2.size()) {118 return false;119 }120 121 // 使用Map统计元素出现次数122 Map<T, Integer> countMap = new HashMap<>();123 for (T element : c1) {124 countMap.merge(element, 1, Integer::sum);125 }126 127 for (T element : c2) {128 Integer count = countMap.get(element);129 if (count == null || count == 0) {130 return false;131 }132 countMap.put(element, count - 1);133 }134 135 return true;136 }137 138 /**139 * 找出集合中的重复元素140 * @param collection 要检查的集合141 * @return 包含重复元素的Set142 */143 public static <T> Set<T> findDuplicates(Collection<T> collection) {144 if (isEmpty(collection)) {145 return new HashSet<>();146 }147 148 Set<T> seen = new HashSet<>();149 Set<T> duplicates = new HashSet<>();150 151 for (T element : collection) {152 if (!seen.add(element)) {153 duplicates.add(element);154 }155 }156 157 return duplicates;158 }159 160 /**161 * 随机选择集合中的元素162 * @param collection 要选择的集合163 * @param count 要选择的元素数量164 * @return 随机选择的元素List165 */166 public static <T> List<T> randomSample(Collection<T> collection, int count) {167 if (isEmpty(collection) || count <= 0) {168 return new ArrayList<>();169 }170 171 List<T> list = new ArrayList<>(collection);172 if (count >= list.size()) {173 return list;174 }175 176 List<T> result = new ArrayList<>();177 Random random = new Random();178 179 for (int i = 0; i < count; i++) {180 int index = random.nextInt(list.size());181 result.add(list.get(index));182 list.remove(index);183 }184 185 return result;186 }187}集合操作方法对比
| 方法 | 功能 | 时间复杂度 | 适用场景 |
|---|---|---|---|
intersection() | 计算交集 | O(n+m) | 找出共同元素 |
union() | 计算并集 | O(n+m) | 合并两个集合 |
difference() | 计算差集 | O(n+m) | 找出独有元素 |
symmetricDifference() | 计算对称差集 | O(n+m) | 找出不同元素 |
partition() | 分割集合 | O(n) | 分批处理数据 |
findDuplicates() | 找出重复元素 | O(n) | 找出重复数据 |
1## 5. 实际应用场景23### 5.1 数据处理和分析45```java title="数据处理和分析示例"6public class DataProcessingExample {7 public static void main(String[] args) {8 // 用户数据统计9 List<User> users = Arrays.asList(10 new User("Alice", 25, "Engineer"),11 new User("Bob", 30, "Manager"),12 new User("Charlie", 25, "Engineer"),13 new User("David", 35, "Director"),14 new User("Eve", 28, "Engineer")15 );16 17 // 使用Collections工具类进行数据分析18 System.out.println("=== 用户数据分析 ===");19 20 // 按年龄分组统计21 Map<Integer, Long> ageDistribution = users.stream()22 .collect(Collectors.groupingBy(User::getAge, Collectors.counting()));23 System.out.println("年龄分布: " + ageDistribution);24 25 // 找出重复的职业26 List<String> jobs = users.stream().map(User::getJob).collect(Collectors.toList());27 Set<String> duplicateJobs = CollectionOperations.findDuplicates(jobs);28 System.out.println("重复职业: " + duplicateJobs);29 30 // 按职业分组,找出每个职业的平均年龄31 Map<String, Double> avgAgeByJob = users.stream()32 .collect(Collectors.groupingBy(33 User::getJob,34 Collectors.averagingInt(User::getAge)35 ));36 System.out.println("各职业平均年龄: " + avgAgeByJob);37 38 // 找出年龄最大的用户39 User oldestUser = Collections.max(users, Comparator.comparing(User::getAge));40 System.out.println("年龄最大的用户: " + oldestUser);41 42 // 按年龄排序43 List<User> sortedByAge = new ArrayList<>(users);44 Collections.sort(sortedByAge, Comparator.comparing(User::getAge));45 System.out.println("按年龄排序: " + sortedByAge);46 }47}4849class User {50 private String name;51 private int age;52 private String job;53 54 // 构造函数、getter、setter省略...55}5.2 缓存和配置管理
1public class CacheConfigExample {2 private static final Map<String, Object> configCache = Collections.synchronizedMap(new HashMap<>());3 private static final Set<String> blacklist = Collections.newSetFromMap(new ConcurrentHashMap<>());4 5 public static void main(String[] args) {6 // 配置缓存管理7 System.out.println("=== 配置缓存管理 ===");8 9 // 安全地添加配置10 safePutConfig("database.url", "jdbc:mysql://localhost:3306/test");11 safePutConfig("cache.ttl", 3600);12 safePutConfig("max.connections", 100);13 14 // 批量获取配置15 List<String> keys = Arrays.asList("database.url", "cache.ttl", "max.connections");16 Map<String, Object> configs = getConfigs(keys);17 System.out.println("获取的配置: " + configs);18 19 // 黑名单管理20 addToBlacklist("spam@example.com");21 addToBlacklist("malware@example.com");22 addToBlacklist("phishing@example.com");23 24 System.out.println("黑名单大小: " + blacklist.size());25 System.out.println("是否在黑名单中: " + isBlacklisted("spam@example.com"));26 27 // 配置验证28 validateConfigs(configs);29 }30 31 private static void safePutConfig(String key, Object value) {32 if (CollectionUtils.isNotEmpty(key) && value != null) {33 configCache.put(key, value);34 }35 }36 37 private static Map<String, Object> getConfigs(List<String> keys) {38 if (CollectionUtils.isEmpty(keys)) {39 return Collections.emptyMap();40 }41 42 return keys.stream()43 .filter(configCache::containsKey)44 .collect(Collectors.toMap(key -> key, configCache::get));45 }46 47 private static void addToBlacklist(String email) {48 if (CollectionUtils.isNotEmpty(email)) {49 blacklist.add(email);50 }51 }52 53 private static boolean isBlacklisted(String email) {54 return CollectionUtils.isNotEmpty(email) && blacklist.contains(email);55 }56 57 private static void validateConfigs(Map<String, Object> configs) {58 if (CollectionUtils.isEmpty(configs)) {59 System.out.println("配置为空,跳过验证");60 return;61 }62 63 // 验证必需的配置项64 Set<String> requiredKeys = new HashSet<>(Arrays.asList("database.url", "max.connections"));65 Set<String> missingKeys = CollectionOperations.difference(requiredKeys, configs.keySet());66 67 if (CollectionUtils.isNotEmpty(missingKeys)) {68 System.out.println("缺少必需配置: " + missingKeys);69 } else {70 System.out.println("所有必需配置都已存在");71 }72 }73}5.3 批量数据处理
1public class BatchProcessingExample {2 public static void main(String[] args) {3 // 模拟大量数据4 List<String> largeDataset = generateLargeDataset(10000);5 System.out.println("=== 批量数据处理 ===");6 System.out.println("数据集大小: " + largeDataset.size());7 8 // 分批处理数据9 int batchSize = 1000;10 List<List<String>> batches = CollectionOperations.partition(largeDataset, batchSize);11 System.out.println("分成 " + batches.size() + " 个批次");12 13 // 并行处理每个批次14 List<String> processedResults = batches.parallelStream()15 .map(batch -> processBatch(batch))16 .flatMap(List::stream)17 .collect(Collectors.toList());18 19 System.out.println("处理完成,结果数量: " + processedResults.size());20 21 // 数据去重和统计22 Set<String> uniqueResults = new HashSet<>(processedResults);23 System.out.println("去重后结果数量: " + uniqueResults.size());24 25 // 找出重复的数据26 Set<String> duplicates = CollectionOperations.findDuplicates(processedResults);27 System.out.println("重复数据数量: " + duplicates.size());28 29 // 随机采样验证30 List<String> sample = CollectionOperations.randomSample(uniqueResults, 10);31 System.out.println("随机采样结果: " + sample);32 }33 34 private static List<String> generateLargeDataset(int size) {35 List<String> dataset = new ArrayList<>();36 Random random = new Random();37 38 for (int i = 0; i < size; i++) {39 dataset.add("data_" + random.nextInt(1000));40 }41 42 return dataset;43 }44 45 private static List<String> processBatch(List<String> batch) {46 // 模拟批处理逻辑47 return batch.stream()48 .map(data -> "processed_" + data)49 .collect(Collectors.toList());50 }51}6. 最佳实践总结
6.1 工具类设计原则
集合工具类的设计遵循以下原则:
- 单一职责:每个方法只做一件事
- 空安全:所有方法都要处理null和空集合
- 性能优化:选择合适的算法和数据结构
- 易用性:提供直观的API接口
6.2 性能优化策略
| 优化策略 | 具体方法 | 适用场景 |
|---|---|---|
| 预分配容量 | 使用 new ArrayList<>(expectedSize) | 已知集合大小 |
| 选择合适的集合 | HashSet用于查找,TreeSet用于排序 | 根据使用场景选择 |
| 并行处理 | 使用 parallelStream() | 大数据量处理 |
| 避免装箱拆箱 | 使用基本类型数组和Stream | 数值计算 |
| 缓存结果 | 缓存频繁计算的结果 | 重复计算 |
6.3 常见陷阱和解决方案
在使用集合工具类时,需要注意以下常见陷阱:
-
Arrays.asList()的不可变性
java1// 错误用法2List<String> list = Arrays.asList("a", "b", "c");3list.add("d"); // 抛出UnsupportedOperationException45// 正确用法6List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c"));7list.add("d"); // 正常工作 -
Collections.synchronizedXXX()的局限性
java1// 注意:单个操作是线程安全的,但复合操作不是2List<String> syncList = Collections.synchronizedList(new ArrayList<>());34// 错误:复合操作不是线程安全的5if (!syncList.contains("item")) {6 syncList.add("item"); // 可能重复添加7}89// 正确:使用同步块10synchronized(syncList) {11 if (!syncList.contains("item")) {12 syncList.add("item");13 }14} -
Stream API的延迟执行
java1// 注意:Stream是延迟执行的2Stream<String> stream = list.stream().filter(s -> s.length() > 5);3list.add("newItem"); // 这会影响stream的结果45// 正确:先收集结果6List<String> result = list.stream().filter(s -> s.length() > 5).collect(Collectors.toList());7list.add("newItem"); // 不会影响已收集的结果
6.4 测试和调试建议
-
单元测试覆盖
- 测试边界条件(空集合、null值)
- 测试异常情况
- 测试性能关键路径
-
调试技巧
- 使用
Arrays.toString()和Arrays.deepToString()打印数组 - 使用
Collections.toString()打印集合 - 使用Stream API的
peek()方法调试流操作
- 使用
-
性能监控
- 监控集合操作的时间复杂度
- 使用JMH进行性能基准测试
- 监控内存使用情况
7. 总结
Java集合工具类为集合操作提供了强大而便捷的功能。通过合理使用Collections和Arrays工具类,我们可以:
- 提高开发效率:避免重复编写集合操作代码
- 保证代码质量:使用经过充分测试的标准库方法
- 优化性能:选择合适的算法和数据结构
- 增强可维护性:统一的API风格和错误处理
在实际开发中,我们应该:
- 优先使用标准库:Collections和Arrays工具类已经覆盖了大部分常见需求
- 合理封装:对于特定业务场景,可以基于标准工具类进行二次封装
- 注意性能:了解各种操作的时间复杂度,选择合适的实现
- 保持一致性:在项目中统一使用相同的工具类和方法
通过深入理解和熟练运用这些工具类,我们能够编写出更加高效、健壮和易维护的Java代码。
参与讨论