Skip to content

集合与数组:何时用谁

数组够用吗?

先问自己一个问题:你的数据是「固定大小」还是「动态增减」?

如果数据量在编译时就确定了,数组完全够用。但如果需要不停地添加、删除元素,数组的固定长度就成了噩梦——每次扩容都要手动处理,代码写起来又臭又长。

更关键的是:数组只解决「存」的问题,而集合框架解决的是「怎么高效地存取、查找、去重、排序」这一整套问题。


核心区别

维度数组集合
大小固定,创建时确定动态增长
类型编译时检查泛型保证
性能随机访问最快有开销但更灵活
功能基本 CRUD丰富(去重/排序/搜索)
基础类型直接存储需要装箱(Integer/Long)

举几个具体的坑:

java
// 数组存储基本类型,无装箱开销
int[] scores = {90, 85, 78}; // 内存紧凑

// 集合存储基本类型,需要装箱
List<Integer> scoreList = List.of(90, 85, 78); // 每个元素都是对象

内存占用差距有多大?假设 100 万个整数:

  • int[] 数组:约 4MB
  • List<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 综合性能最优。


总结口诀

固定大小用数组,基本类型用数组,内存敏感用数组。动态增删用集合,快速查找用集合,去重排序用集合。

二者并非对立关系——经常是先用数组批量处理,再用集合做后续操作。


相关链接

基于 VitePress 构建