集合与数组:何时用谁
数组够用吗?
先问自己一个问题:你的数据是「固定大小」还是「动态增减」?
如果数据量在编译时就确定了,数组完全够用。但如果需要不停地添加、删除元素,数组的固定长度就成了噩梦——每次扩容都要手动处理,代码写起来又臭又长。
更关键的是:数组只解决「存」的问题,而集合框架解决的是「怎么高效地存取、查找、去重、排序」这一整套问题。
核心区别
| 维度 | 数组 | 集合 |
|---|---|---|
| 大小 | 固定,创建时确定 | 动态增长 |
| 类型 | 编译时检查 | 泛型保证 |
| 性能 | 随机访问最快 | 有开销但更灵活 |
| 功能 | 基本 CRUD | 丰富(去重/排序/搜索) |
| 基础类型 | 直接存储 | 需要装箱(Integer/Long) |
举几个具体的坑:
java
// 数组存储基本类型,无装箱开销
int[] scores = {90, 85, 78}; // 内存紧凑
// 集合存储基本类型,需要装箱
List<Integer> scoreList = List.of(90, 85, 78); // 每个元素都是对象内存占用差距有多大?假设 100 万个整数:
int[]数组:约 4MBList<Integer>集合:约 24MB(对象头 + 引用 + 装箱)
互转:5 秒学会
java
import java.util.*;
public class ArrayCollectionConvert {
public static void main(String[] args) {
String[] languages = {"Java", "Python", "Go"};
// 数组 → List(两种方式,差别很大)
List<String> list1 = Arrays.asList(languages); // 固定大小,可修改元素
List<String> list2 = new ArrayList<>(Arrays.asList(languages)); // 真正可变
// 数组 → Set(去重)
Set<String> set = new HashSet<>(Arrays.asList(languages));
// List → 数组
List<Integer> nums = List.of(1, 2, 3);
Integer[] numArray = nums.toArray(new Integer[0]);
// JDK 16+ 更简洁
Integer[] numArray2 = nums.toArray(Integer[]::new);
}
}一个经典坑
Arrays.asList() 返回的列表不能增删元素:
java
List<String> fixed = Arrays.asList("a", "b");
fixed.add("c"); // UnsupportedOperationException!
// 想增删?必须包一层
List<String> mutable = new ArrayList<>(Arrays.asList("a", "b"));
mutable.add("c"); // 正常实战场景:怎么选
用数组的场景
java
// 场景1:已知大小的坐标点
Point[] points = new Point[100];
// 场景2:性能敏感的数值计算
double[] vectors = new double[1024];
for (int i = 0; i < 1024; i++) {
vectors[i] = Math.sin(i * angle);
}
// 场景3:基本类型的大数据处理
int[] bitmap = new int[1 << 20]; // 4MB 位图用集合的场景
java
// 场景1:不确定数量的数据收集
Set<String> tags = new HashSet<>();
for (String line : logLines) {
tags.add(extractTag(line));
}
// 场景2:需要快速查找
Map<String, User> userMap = new HashMap<>();
userMap.put(user.getId(), user);
// O(1) 查找,比数组 O(n) 快太多
// 场景3:需要去重且保持顺序
List<String> unique = new ArrayList<>(new LinkedHashSet<>(allItems));
// 场景4:需要自动排序
NavigableSet<Integer> sorted = new TreeSet<>();
sorted.addAll(ids);
Integer lower = sorted.lower(50); // 找小于 50 的最大数性能对比:一次说清楚
同样是查找元素,数组和不同集合差距有多大?
java
import java.util.*;
public class PerformanceComparison {
public static void main(String[] args) {
int size = 1_000_000;
// 数组(有序)
Integer[] array = new Integer[size];
Arrays.setAll(array, i -> i);
// ArrayList
List<Integer> list = new ArrayList<>(Arrays.asList(array));
// HashSet
Set<Integer> set = new HashSet<>(list);
int target = size - 1;
// 数组:二分查找
long start = System.nanoTime();
int idx = Arrays.binarySearch(array, target);
System.out.println("数组二分查找: " + (System.nanoTime() - start) / 1000 + " μs");
// ArrayList:线性查找
start = System.nanoTime();
list.contains(target);
System.out.println("ArrayList 查找: " + (System.nanoTime() - start) / 1000 + " μs");
// HashSet:哈希查找
start = System.nanoTime();
set.contains(target);
System.out.println("HashSet 查找: " + (System.nanoTime() - start) / 1000 + " μs");
}
}典型结果(100 万元素,查找最后那个):
数组二分查找: ~2 μs
ArrayList 查找: ~500 μs
HashSet 查找: ~0.5 μs结论:有序数组 + 二分查找 性能极好,但代价是插入/删除 O(n)。HashSet 综合性能最优。
总结口诀
固定大小用数组,基本类型用数组,内存敏感用数组。动态增删用集合,快速查找用集合,去重排序用集合。
二者并非对立关系——经常是先用数组批量处理,再用集合做后续操作。
