文章目录

Java 常用集合:ArrayList 与 HashMap 的使用

发布于 2026-04-05 13:06:55 · 浏览 209 次 · 评论 0 条

Java 常用集合:ArrayList 与 HashMap 的使用

在 Java 开发中,集合是存储和操作数据的基础工具。ArrayList 和 HashMap 是最常用的两种集合类型,几乎出现在每一个 Java 项目中。理解它们的特性和适用场景,能够帮助你写出更高效、更易维护的代码。


一、ArrayList:动态数组

ArrayList 是 Java 中最常用的 List 接口实现类,本质上是一个动态数组。它允许存储重复元素,且保持元素的插入顺序。

1.1 基本特性

特性 说明
数据结构 数组(Object[])
元素有序 是(按插入顺序)
元素唯一 否(允许重复)
随机访问 支持(通过索引,时间复杂度 O(1))
线程安全 否(线程不安全)

1.2 常用操作

创建 ArrayList 并添加元素

import java.util.ArrayList;
import java.util.List;

public class ArrayListExample {
    public static void main(String[] args) {
        // 创建 ArrayList(推荐使用泛型指定类型)
        List<String> fruits = new ArrayList<>();

        // 添加元素
        fruits.add("苹果");
        fruits.add("香蕉");
        fruits.add("橙子");

        System.out.println(fruits); // [苹果, 香蕉, 橙子]
    }
}

根据索引访问和修改元素

// 获取指定索引的元素
String first = fruits.get(0); // "苹果"

// 修改指定索引的元素
fruits.set(1, "葡萄"); // 将索引1的元素改为"葡萄"

// 删除元素(按索引或按对象)
fruits.remove(0);    // 删除索引0的元素
fruits.remove("橙子"); // 删除值为"橙子"的元素

遍历 ArrayList

// 方式1:普通 for 循环
for (int i = 0; i < fruits.size(); i++) {
    System.out.println(fruits.get(i));
}

// 方式2:增强 for 循环(推荐)
for (String fruit : fruits) {
    System.out.println(fruit);
}

// 方式3:Lambda 表达式(Java 8+)
fruits.forEach(fruit -> System.out.println(fruit));

1.3 注意事项

ArrayList 在中间位置插入或删除元素时,需要移动后续所有元素,时间复杂度为 O(n)。如果你的业务场景涉及频繁的中间插入删除操作,考虑使用 LinkedList 替代。


二、HashMap:键值对映射

HashMap 是 Map 接口最常用的实现类,基于哈希表结构实现。它以键值对的形式存储数据,每个键唯一,对应的值可以重复。

2.1 基本特性

特性 说明
数据结构 数组 + 链表/红黑树(JDK 1.8+)
键 唯一(null 键仅允许一个)
值 可重复(null 值允许多个)
查找速度 O(1)(平均情况)
线程安全 否

2.2 常用操作

创建 HashMap 并添加元素

import java.util.HashMap;
import java.util.Map;

public class HashMapExample {
    public static void main(String[] args) {
        // 创建 HashMap
        Map<String, Integer> studentScores = new HashMap<>();

        // 添加键值对
        studentScores.put("张三", 95);
        studentScores.put("李四", 88);
        studentScores.put("王五", 92);

        System.out.println(studentScores); // {张三=95, 李四=88, 王五=92}
    }
}

根据键访问和修改值

// 根据键获取值
Integer score = studentScores.get("张三"); // 95

// 键不存在时返回 null,可设置默认值
Integer defaultScore = studentScores.getOrDefault("赵六", 0); // 0

// 修改键对应的值
studentScores.put("李四", 90); // 更新李四的分数

// 只有键不存在时才添加
studentScores.putIfAbsent("赵六", 85);

删除和检查元素

// 根据键删除
studentScores.remove("王五");

// 检查键是否存在
boolean hasZhangSan = studentScores.containsKey("张三");

// 检查值是否存在
boolean has90 = studentScores.containsValue(90);

// 清空所有元素
studentScores.clear();

遍历 HashMap

// 方式1:遍历键
for (String name : studentScores.keySet()) {
    System.out.println("姓名: " + name);
}

// 方式2:遍历值
for (Integer score : studentScores.values()) {
    System.out.println("分数: " + score);
}

// 方式3:遍历键值对(推荐)
for (Map.Entry<String, Integer> entry : studentScores.entrySet()) {
    System.out.println(entry.getKey() + " 的分数是 " + entry.getValue());
}

// 方式4:Lambda 表达式
studentScores.forEach((name, score) -> 
    System.out.println(name + ": " + score));

2.3 注意事项

HashMap 的性能高度依赖 hashCode() 方法的实现质量。重写 equals() 方法时,必须同时重写 hashCode() 方法,保证相等的对象返回相同的哈希值,否则会导致 HashMap 无法正常工作。


三、核心区别与选择指南

理解 ArrayList 和 HashMap 的本质区别,是正确使用它们的前提。

3.1 核心区别

对比维度 ArrayList HashMap
数据结构 单列集合 键值对映射
访问方式 通过索引访问 通过键访问
查找效率 O(n)(需要遍历) O(1)(平均情况)
插入顺序 保持 不保证(JDK 7 及之前甚至完全无序)

3.2 使用场景选择

选择 ArrayList 的场景

  • 需要按顺序存储和访问元素
  • 主要通过索引进行随机访问
  • 允许存储重复元素
  • 业务逻辑更接近"列表"概念(如待办事项列表、商品列表)

选择 HashMap 的场景

  • 需要通过唯一键快速查找对应值
  • 需要存储键值对形式的数据(如配置信息、缓存数据)
  • 对查询效率有较高要求
  • 需要根据某个唯一标识快速定位数据(如用户 ID -> 用户对象)

四、实战示例:学生信息管理系统

下面通过一个实际场景,展示 ArrayList 和 HashMap 如何配合使用。

import java.util.*;

public class StudentManager {
    // 使用 ArrayList 存储所有学生(保持插入顺序)
    private List<Student> studentList = new ArrayList<>();

    // 使用 HashMap 通过学号快速查找学生
    private Map<String, Student> studentMap = new HashMap<>();

    public void addStudent(String id, String name, int age) {
        Student student = new Student(id, name, age);

        studentList.add(student);
        studentMap.put(id, student);

        System.out.println("添加成功: " + name);
    }

    public Student getStudentById(String id) {
        return studentMap.get(id);
    }

    public List<Student> getAllStudents() {
        return new ArrayList<>(studentList); // 返回副本,避免外部修改
    }

    public void removeStudent(String id) {
        Student removed = studentMap.remove(id);
        if (removed != null) {
            studentList.remove(removed);
            System.out.println("删除成功: " + removed.getName());
        }
    }

    public static void main(String[] args) {
        StudentManager manager = new StudentManager();

        manager.addStudent("S001", "张三", 20);
        manager.addStudent("S002", "李四", 21);
        manager.addStudent("S003", "王五", 19);

        // 快速通过学号查找
        Student s = manager.getStudentById("S002");
        if (s != null) {
            System.out.println("找到学生: " + s.getName());
        }

        // 遍历所有学生
        System.out.println("\n所有学生信息:");
        manager.getAllStudents().forEach(stu -> 
            System.out.println(stu.getId() + " - " + stu.getName()));
    }
}

class Student {
    private String id;
    private String name;
    private int age;

    public Student(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public String getId() { return id; }
    public String getName() { return name; }
    public int getAge() { return age; }
}

这个示例充分发挥了两种集合的优势:ArrayList 维护了学生的添加顺序,便于按序展示;HashMap 提供了 O(1) 的查找速度,便于快速定位。


五、进阶技巧

5.1 初始化集合

// 简洁的初始化方式(Java 9+)
List<String> list = List.of("a", "b", "c");
Map<String, Integer> map = Map.of("a", 1, "b", 2);

// 双大括号初始化(了解即可,不推荐生产使用)
List<String> list2 = new ArrayList<String>() {{
    add("a");
    add("b");
}};

5.2 集合转数组/数组转集合

List<String> list = Arrays.asList("a", "b", "c");

// 集合转数组
String[] array = list.toArray(new String[0]);

// 数组转集合
Integer[] nums = {1, 2, 3};
List<Integer> numList = Arrays.asList(nums);

5.3 线程安全的集合

在多线程环境下,ArrayList 和 HashMap 是不安全的。可使用以下替代方案:

import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentHashMap;

// 线程安全的 List
List<String> safeList = new CopyOnWriteArrayList<>();

// 线程安全的 Map
Map<String, Integer> safeMap = new ConcurrentHashMap<>();

六、性能调优建议

使用集合时注意以下性能要点:

  1. 指定初始容量:ArrayList 和 HashMap 扩容有性能开销。创建时根据预估大小指定初始容量,减少扩容次数。
// 预估需要存储1000个元素
List<String> list = new ArrayList<>(1000);
Map<String, Object> map = new HashMap<>(1000, 0.8f);
  1. 选择合适的负载因子:HashMap 默认负载因子 0.75 是时间和空间成本的平衡点。对于追求内存效率的场景,可适当降低。

  2. 避免在循环中频繁调用 size():对于 ArrayList,将 size() 缓存到局部变量,减少方法调用开销。

// 低效
for (int i = 0; i < list.size(); i++) { ... }

// 高效
int size = list.size();
for (int i = 0; i < size; i++) { ... }

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文