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<>();
六、性能调优建议
使用集合时注意以下性能要点:
- 指定初始容量:
ArrayList和HashMap扩容有性能开销。创建时根据预估大小指定初始容量,减少扩容次数。
// 预估需要存储1000个元素
List<String> list = new ArrayList<>(1000);
Map<String, Object> map = new HashMap<>(1000, 0.8f);
-
选择合适的负载因子:
HashMap默认负载因子 0.75 是时间和空间成本的平衡点。对于追求内存效率的场景,可适当降低。 -
避免在循环中频繁调用 size():对于
ArrayList,将size()缓存到局部变量,减少方法调用开销。
// 低效
for (int i = 0; i < list.size(); i++) { ... }
// 高效
int size = list.size();
for (int i = 0; i < size; i++) { ... }
暂无评论,快来抢沙发吧!