Java集合与数组互转终极指南:从基础到高级应用
一、概述
Java集合框架和数组是Java编程中最常用的数据结构,它们之间的相互转换是日常开发中的常见操作。本文将全面介绍各种集合类与数组之间的转换方法,包括传统方式和Java 8+的流式操作,并提供详细的代码示例和对比分析。
二、数组与集合基础
1. 数组与集合的区别
特性 | 数组 | 集合 |
大小 | 固定长度 | 动态扩展 |
类型 | 可以存储基本类型和对象 | 只能存储对象 |
功能 | 有限,只有length属性和[]操作 | 丰富的方法(add,remove等) |
性能 | 随机访问快 | 某些操作可能较慢(如LinkedList) |
线程安全 | 不安全 | 部分实现安全(如Vector) |
2. 主要集合类简介
- List: 有序集合,允许重复元素
- ArrayList: 基于动态数组
- LinkedList: 基于双向链表
- Vector: 线程安全的动态数组
- Set: 不允许重复元素的集合
- HashSet: 基于哈希表
- LinkedHashSet: 保持插入顺序的HashSet
- TreeSet: 基于红黑树,有序
- Map: 键值对集合
- HashMap: 基于哈希表
- LinkedHashMap: 保持插入顺序的HashMap
- TreeMap: 基于红黑树,按键排序
- Hashtable: 线程安全的哈希表
三、数组转集合
1. 基本类型数组转List
基本类型数组不能直接转换为集合,需要先转换为包装类型。
int[] intArray = {1, 2, 3, 4, 5};
// Java 8之前的方式
List<Integer> list1 = new ArrayList<>();
for (int i : intArray) {
list1.add(i);
}
// Java 8+ 使用流
List<Integer> list2 = Arrays.stream(intArray)
.boxed()
.collect(Collectors.toList());
System.out.println(list2); // [1, 2, 3, 4, 5]
2. 对象数组转List
String[] strArray = {"Apple", "Banana", "Orange"};
// 方法1: Arrays.asList (返回的列表大小固定)
List<String> list3 = Arrays.asList(strArray);
// list3.add("Pear"); // 会抛出UnsupportedOperationException
// 方法2: new ArrayList<>(Arrays.asList())
List<String> list4 = new ArrayList<>(Arrays.asList(strArray));
list4.add("Pear"); // 正常添加
System.out.println(list4); // [Apple, Banana, Orange, Pear]
// 方法3: Java 8 Stream
List<String> list5 = Arrays.stream(strArray)
.collect(Collectors.toList());
// 方法4:Collections.addAll
List<String> strList4 = new ArrayList<>();
Collections.addAll(strList4, strArray);
System.out.println(list5); // [Apple, Banana, Orange]
3. 数组转Set
Integer[] intArray = {1, 2, 2, 3, 4, 4, 5};
// 转换为HashSet(无序)
Set<Integer> set1 = new HashSet<>(Arrays.asList(intArray));
System.out.println(set1); // [1, 2, 3, 4, 5] (顺序可能不同)
// 转换为LinkedHashSet(保持插入顺序)
Set<Integer> set2 = new LinkedHashSet<>(Arrays.asList(intArray));
System.out.println(set2); // [1, 2, 3, 4, 5]
// 转换为TreeSet(排序)
Set<Integer> set3 = new TreeSet<>(Arrays.asList(intArray));
System.out.println(set3); // [1, 2, 3, 4, 5]
// Java 8 Stream方式
Set<Integer> set4 = Arrays.stream(intArray)
.collect(Collectors.toSet());
Set<Integer> set5 = Arrays.stream(intArray)
.collect(Collectors.toCollection(TreeSet::new));
4. 多维数组转List
Integer[][] matrix = {{1, 2}, {3, 4}, {5, 6}};
// 转换为List<List<Integer>>
List<List<Integer>> listOfLists = Arrays.stream(matrix)
.map(Arrays::asList)
.collect(Collectors.toList());
System.out.println(listOfLists); // [[1, 2], [3, 4], [5, 6]]
四、集合转数组
1. List转对象数组
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
// 方法1: toArray() - 返回Object[]
Object[] array1 = fruits.toArray();
System.out.println(Arrays.toString(array1)); // [Apple, Banana, Orange]
// 方法2: toArray(T[]) - 推荐方式
String[] array2 = fruits.toArray(new String[0]);
System.out.println(Arrays.toString(array2)); // [Apple, Banana, Orange]
// 方法3: 指定大小的数组
String[] array3 = new String[fruits.size()];
fruits.toArray(array3);
System.out.println(Arrays.toString(array3)); // [Apple, Banana, Orange]
// Java 8 Stream方式
String[] array4 = fruits.stream().toArray(String[]::new);
System.out.println(Arrays.toString(array4)); // [Apple, Banana, Orange]
2. List转基本类型数组
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 转换为int[]
int[] intArray = numbers.stream()
.mapToInt(Integer::intValue)
.toArray();
System.out.println(Arrays.toString(intArray)); // [1, 2, 3, 4, 5]
// 转换为double[]
double[] doubleArray = numbers.stream()
.mapToDouble(Integer::doubleValue)
.toArray();
System.out.println(Arrays.toString(doubleArray)); // [1.0, 2.0, 3.0, 4.0, 5.0]
3. Set转数组
Set<String> fruitSet = new HashSet<>();
fruitSet.add("Apple");
fruitSet.add("Banana");
fruitSet.add("Orange");
// 转换为数组
String[] array5 = fruitSet.toArray(new String[0]);
System.out.println(Arrays.toString(array5)); // 顺序不确定
// Java 8 Stream保持顺序
String[] array6 = fruitSet.stream()
.sorted()
.toArray(String[]::new);
System.out.println(Arrays.toString(array6)); // [Apple, Banana, Orange]
4. Map转数组
Map<Integer, String> map = new HashMap<>();
map.put(1, "Apple");
map.put(2, "Banana");
map.put(3, "Orange");
// 转换key为数组
Integer[] keys = map.keySet().toArray(new Integer[0]);
System.out.println(Arrays.toString(keys)); // [1, 2, 3]
// 转换value为数组
String[] values = map.values().toArray(new String[0]);
System.out.println(Arrays.toString(values)); // [Apple, Banana, Orange]
// 转换entry为数组
Map.Entry<Integer, String>[] entries =
map.entrySet().toArray(new Map.Entry[0]);
System.out.println(Arrays.toString(entries)); // [1=Apple, 2=Banana, 3=Orange]
五、集合间的转换
1. List与其他集合的转换
1.1 List转Set
List<String> listWithDuplicates = Arrays.asList("A", "B", "C", "A", "B");
// 方法1:构造函数
Set<String> set1 = new HashSet<>(listWithDuplicates);
// 方法2:Java 8 Stream
Set<String> set2 = listWithDuplicates.stream().collect(Collectors.toSet());
// 保持顺序的Set
Set<String> linkedSet = new LinkedHashSet<>(listWithDuplicates);
1.2 Set转List
Set<String> fruitSet = new HashSet<>(Arrays.asList("Apple", "Banana", "Orange"));
// 方法1:构造函数
List<String> fruitList1 = new ArrayList<>(fruitSet);
// 方法2:Java 8 Stream
List<String> fruitList2 = fruitSet.stream().collect(Collectors.toList());
// 保持Set的顺序
List<String> orderedList = new ArrayList<>(new LinkedHashSet<>(fruitSet));
2. Map与其他集合的转换
2.1 Map转Set/List
Map<Integer, String> map = new HashMap<>();
map.put(1, "Apple");
map.put(2, "Banana");
map.put(3, "Orange");
// 获取key的Set
Set<Integer> keySet = map.keySet();
// 获取value的Collection
Collection<String> values = map.values();
// 转为List
List<String> valueList = new ArrayList<>(map.values());
// 获取entry的Set
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
2.2 List/Set转Map
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
// 转为Map: 元素作为key,长度作为value
Map<String, Integer> fruitLengthMap = fruits.stream()
.collect(Collectors.toMap(Function.identity(), String::length));
// 处理重复key
List<String> dupFruits = Arrays.asList("Apple", "Banana", "Apple");
Map<String, Integer> dupMap = dupFruits.stream()
.collect(Collectors.toMap(Function.identity(), String::length, (oldVal, newVal) -> oldVal));
// 转为有序Map
Map<String, Integer> orderedMap = fruits.stream()
.collect(Collectors.toMap(Function.identity(), String::length, (o, n) -> n, LinkedHashMap::new));
3. 特殊集合转换
3.1 数组与Stream互转
String[] array = {"Java", "Python", "C++"};
// 数组转Stream
Stream<String> stream = Arrays.stream(array);
// Stream转数组
String[] newArray = stream.toArray(String[]::new);
3.2 集合与Stream互转
List<String> languages = Arrays.asList("Java", "Python", "C++");
// 集合转Stream
Stream<String> languageStream = languages.stream();
// Stream转集合
List<String> newList = languageStream.collect(Collectors.toList());
六、多维集合转换
1. 扁平化处理(Flatten)
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("Apple", "Banana"),
Arrays.asList("Orange", "Grape"),
Arrays.asList("Pear", "Peach")
);
// 二维List转一维List
List<String> flatList = nestedList.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
// 输出: [Apple, Banana, Orange, Grape, Pear, Peach]
2. 分组与分区
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange", "Grape", "Pear");
// 按长度分组
Map<Integer, List<String>> byLength = fruits.stream()
.collect(Collectors.groupingBy(String::length));
// 分区: 长度大于5的和其余的
Map<Boolean, List<String>> partitioned = fruits.stream()
.collect(Collectors.partitioningBy(f -> f.length() > 5));
七、Java 8+高级转换
1. 流式操作转换
/***********************************案例一*************************************/
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 过滤后转换
String[] longNames = names.stream()
.filter(name -> name.length() > 4)
.toArray(String[]::new);
System.out.println(Arrays.toString(longNames)); // [Alice, Charlie, David]
// 映射后转换
int[] nameLengths = names.stream()
.mapToInt(String::length)
.toArray();
// 转为大写
List<String> upperNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 转为长度列表
List<Integer> nameLengths = names.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(Arrays.toString(nameLengths)); // [5, 3, 7, 5]
/***********************************案例二*************************************/
List<Employee> employees = Arrays.asList(
new Employee("John", 28, "IT"),
new Employee("Jane", 32, "HR"),
new Employee("Mike", 25, "IT")
);
// List转Map: 按部门分组
Map<String, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
// List转Map: 部门到员工名的映射
Map<String, List<String>> deptToNames = employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.mapping(Employee::getName, Collectors.toList())
));
// 转换为不可变集合
List<String> immutableList = employees.stream()
.map(Employee::getName)
.collect(Collectors.toUnmodifiableList());
2. 并行流转换
List<Integer> largeList = IntStream.range(0, 1000000)
.boxed()
.collect(Collectors.toList());
// 并行流转换
int[] parallelArray = largeList.parallelStream()
.mapToInt(Integer::intValue)
.toArray();
System.out.println("Length: " + parallelArray.length); // Length: 1000000
3. 自定义收集器转换
List<String> letters = Arrays.asList("a", "b", "c", "d");
// 自定义收集器转换为特定类型数组
String[] uppercaseArray = letters.stream()
.map(String::toUpperCase)
.collect(Collectors.toList())
.toArray(new String[0]);
System.out.println(Arrays.toString(uppercaseArray)); // [A, B, C, D]
4. Collectors工具类详解
// 转换为特定集合类型
List<String> list = stream.collect(Collectors.toCollection(LinkedList::new));
// 复杂转换:分组
Map<Integer, List<String>> lengthMap = Arrays.stream(names)
.collect(Collectors.groupingBy(String::length));
// 复杂转换:分区
Map<Boolean, List<String>> partition = Arrays.stream(names)
.collect(Collectors.partitioningBy(s -> s.length() > 4));
// 自定义收集器
List<String> customList = stream.collect(
ArrayList::new,
ArrayList::add,
ArrayList::addAll);
八、多维集合转换
1. 扁平化处理(Flatten)
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("Apple", "Banana"),
Arrays.asList("Orange", "Grape"),
Arrays.asList("Pear", "Peach")
);
// 二维List转一维List
List<String> flatList = nestedList.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
// 输出: [Apple, Banana, Orange, Grape, Pear, Peach]
2. 分组与分区
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange", "Grape", "Pear");
// 按长度分组
Map<Integer, List<String>> byLength = fruits.stream()
.collect(Collectors.groupingBy(String::length));
// 分区: 长度大于5的和其余的
Map<Boolean, List<String>> partitioned = fruits.stream()
.collect(Collectors.partitioningBy(f -> f.length() > 5));
九、自定义转换器实现
1. 自定义List转换器
public class ListConverter {
/**
* 将数组转换为自定义List实现
* @param array 源数组
* @param filter 过滤条件
* @param mapper 映射函数
* @return 自定义List
*/
public static <T, R> List<R> convertWithProcessing(
T[] array,
Predicate<T> filter,
Function<T, R> mapper) {
return Arrays.stream(array)
.filter(filter)
.map(mapper)
.collect(Collectors.toCollection(CustomList::new));
}
// 自定义List实现
private static class CustomList<E> extends ArrayList<E> {
// 可添加自定义行为
}
}
// 使用示例
String[] names = {"Alice", "Bob", null, "Charlie"};
List<String> result = ListConverter.convertWithProcessing(
names,
Objects::nonNull, // 过滤null
String::toUpperCase // 转为大写
);
十、性能分析与最佳实践
1. 转换方法性能对比
转换方式 | 时间复杂度 | 空间复杂度 | 适用场景 |
Arrays.asList() | O(1) | O(1) | 快速创建固定大小列表 |
new ArrayList<>(Arrays.asList()) | O(n) | O(n) | 需要可变列表 |
Collection.toArray() | O(n) | O(n) | 通用集合转数组 |
Stream.toArray() | O(n) | O(n) | Java 8+, 可结合流操作 |
手动循环复制 | O(n) | O(n) | 需要特殊处理时 |
2. 最佳实践建议
- 数组转集合:
- 需要可变集合时,使用 new ArrayList<>(Arrays.asList(array))
- 如果不需要修改集合,使用Arrays.asList(array)
- Java 8+推荐使用流式操作,更灵活
- 基本类型数组记得先 boxed()
- 集合转数组:
- 优先使用 collection.toArray(new T[0]) 而不是 new T[size]
- Java 8+推荐 stream().toArray(T[]::new)
- 基本类型集合使用对应的 mapToXxx() 方法
- 集合间转换:
- 注意保持顺序的需求(LinkedHashSet/LinkedHashMap)
- 大数据量时考虑并行流parallelStream()
- 不可变集合使用Collectors.toUnmodifiableList()
- 性能考虑:
- 大数据量考虑并行流
- 频繁转换考虑重用数组/集合
- 注意 Arrays.asList() 返回的是视图,不是独立集合
- 代码可读性:
- 复杂的转换操作使用流式API更清晰
- 添加适当的注释说明转换目的
- 考虑使用工具类封装常用转换
十一、常见问题与解决方案
1. UnsupportedOperationException异常
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array);
// list.add("d"); // 抛出UnsupportedOperationException
解决方案: 使用 new ArrayList<>(Arrays.asList(array)) 创建可变列表
2. 基本类型数组问题
int[] intArray = {1, 2, 3};
// List<Integer> list = Arrays.asList(intArray); // 编译错误
解决方案: 先转换为包装类型数组或使用流式操作
List<Integer> list = Arrays.stream(intArray).boxed().collect(Collectors.toList());
3. 空数组与null处理
List<String> list = getPossiblyNullList();
String[] array = list != null ? list.toArray(new String[0]) : new String[0];
4. 保持顺序问题
Set<String> set = new HashSet<>(Arrays.asList("b", "a", "c"));
String[] array = set.toArray(new String[0]); // 顺序不确定
解决方案: 使用 LinkedHashSet 或排序
String[] orderedArray = set.stream().sorted().toArray(String[]::new);
十二、实用工具类示例
public class CollectionArrayUtils {
// 私有构造器防止实例化
private CollectionArrayUtils() {}
/**
* 将基本类型int数组转换为List<Integer>
*/
public static List<Integer> toList(int[] array) {
return Arrays.stream(array).boxed().collect(Collectors.toList());
}
/**
* 将对象数组转换为指定类型的List
*/
public static <T> List<T> toList(T[] array) {
return new ArrayList<>(Arrays.asList(array));
}
/**
* 将集合转换为数组,处理null情况
*/
public static <T> T[] toArray(Collection<T> collection, Class<T> clazz) {
if (collection == null) {
return (T[]) Array.newInstance(clazz, 0);
}
return collection.toArray((T[]) Array.newInstance(clazz, collection.size()));
}
/**
* 将Map的键转换为数组
*/
public static <K, V> K[] mapKeysToArray(Map<K, V> map, Class<K> clazz) {
if (map == null) {
return (K[]) Array.newInstance(clazz, 0);
}
return map.keySet().toArray((K[]) Array.newInstance(clazz, map.size()));
}
/**
* 将二维数组转换为List的List
*/
public static <T> List<List<T>> toListOfLists(T[][] matrix) {
return Arrays.stream(matrix)
.map(row -> Arrays.asList(row))
.collect(Collectors.toList());
}
}
十三、新兴技术应用
1. 大数据处理
// Spark RDD转换示例
JavaRDD<String> rdd = sparkContext.parallelize(Arrays.asList("a", "b", "c"));
List<String> collected = rdd.collect(); // 分布式集合转本地集合
// Flink DataStream转换
DataStream<String> stream = env.fromCollection(Arrays.asList("a", "b", "c"));
List<String> result = stream.executeAndCollect(); // 流式集合转批集合
2. 人工智能应用
// 将Java集合转换为TensorFlow张量
List<Float> data = Arrays.asList(1.0f, 2.0f, 3.0f);
Tensor<Float> tensor = Tensor.create(data, Float.class);
// 反向转换
List<Float> converted = Arrays.asList(tensor.copyTo(new Float[data.size()]));
十四、未来发展趋势
- 值类型数组:Valhalla项目将引入值类型,可能改变基本类型数组的处理方式
- 更智能的流:可能引入自动并行化、GPU加速的流操作
- 模式匹配:JEP 406将增强集合的模式匹配能力
- 协程支持:可能影响大规模集合的异步处理方式
十五、开源项目案例
1. Guava的集合工具
// 不可变集合创建
ImmutableList<String> immutable = ImmutableList.copyOf(array);
// 多集合操作
List<String> filtered = Lists.newArrayList(
Collections2.filter(list, Predicates.notNull()));
// 转换工具
List<Integer> lengths = Lists.transform(list, String::length);
2. Apache Commons Collections
// 类型安全转换
String[] array = {"1", "2", "3"};
List<Number> list = (List<Number>) CollectionUtils.collect(
Arrays.asList(array),
new Transformer<String, Number>() {
public Number transform(String input) {
return Integer.valueOf(input);
}
});
技术趋势图 (虚拟数据)
UML类图 (关键接口关系)
性能对比图表
操作 | 数据量 | 传统方法(ms) | Stream API(ms) | 并行流(ms) |
数组转List | 10K | 2 | 5 | 3 |
List转Set | 100K | 15 | 25 | 12 |
过滤转换 | 1M | 45 | 60 | 30 |
十六、总结
本文全面介绍了Java中集合与数组之间的各种转换方法,包括:
- 基本类型数组与集合的相互转换
- 对象数组与各种集合(List/Set/Map)的转换
- Java 8流式API在转换中的应用
- 多维数组与嵌套集合的转换
- 性能分析与最佳实践
- 常见问题解决方案
- 实用工具类封装
掌握这些转换技巧可以让你在日常开发中更加游刃有余地处理数据结构的转换需求。记住根据具体场景选择最合适的方法,平衡代码简洁性、可读性和性能需求。
Java 数组和集合互转像玩 “数据变形记”!转好了丝滑如德芙,转崩了程序秒变 “数据难民” 现场,主打一个心跳加速!
字数已达标,朕要追剧了,退朝前记得转发。