1. 项目背景与核心需求
这个案例源于我在教学过程中发现的一个普遍现象:很多Java初学者在学完ArrayList基础语法后,仍然不知道如何在实际项目中运用它。于是我想到了用电影管理系统这个生活化的场景,来演示ArrayList在真实业务中的完整应用链条。
电影管理系统本质上需要解决三个核心问题:
- 如何存储电影信息(片名、导演、主演、类型、评分等)
- 如何实现增删改查等基础功能
- 如何支持复杂的业务查询(如按类型筛选、按评分排序)
ArrayList作为Java集合框架中最常用的动态数组实现,恰好能完美满足这些需求。它的底层是Object[]数组,通过自动扩容机制解决了固定长度数组的局限性,同时提供了丰富的API来操作元素。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统设计与类结构
2.1 领域模型设计
我们先定义两个核心类:
java复制// 电影类
public class Movie {
private String id; // 电影编号
private String title; // 片名
private String director; // 导演
private List<String> actors;// 主演列表
private String type; // 类型
private double rating; // 评分
// 构造方法、getter/setter省略
}
// 管理系统类
public class MovieSystem {
private List<Movie> movies = new ArrayList<>();
// 业务方法
public void addMovie(Movie movie) {...}
public void deleteMovie(String id) {...}
// 其他方法
}
2.2 ArrayList的选型考量
为什么选择ArrayList而不是LinkedList?
- 随机访问需求:电影系统需要频繁按索引或ID查询,ArrayList的O(1)访问效率更高
- 内存局部性:连续内存存储有利于CPU缓存预取
- 实现简单:不需要维护复杂的节点关系
注意:当有频繁的中间位置插入/删除时,LinkedList会更合适。但电影系统的业务特点决定这类操作较少。
3. 核心功能实现
3.1 增删改查基础实现
java复制// 添加电影
public void addMovie(Movie movie) {
// 参数校验
if(movie == null) throw new IllegalArgumentException();
// 检查重复ID
for(Movie m : movies) {
if(m.getId().equals(movie.getId())) {
throw new RuntimeException("电影ID已存在");
}
}
movies.add(movie);
}
// 删除电影
public boolean deleteMovie(String id) {
Iterator<Movie> it = movies.iterator();
while(it.hasNext()) {
if(it.next().getId().equals(id)) {
it.remove();
return true;
}
}
return false;
}
3.2 复杂查询实现
java复制// 按类型查询
public List<Movie> getMoviesByType(String type) {
return movies.stream()
.filter(m -> m.getType().equals(type))
.collect(Collectors.toList());
}
// 按评分排序
public List<Movie> getMoviesSortedByRating() {
return movies.stream()
.sorted((m1,m2) -> Double.compare(m2.getRating(), m1.getRating()))
.collect(Collectors.toList());
}
4. 性能优化实践
4.1 初始容量设置
如果预先知道系统大概会存储1000部电影:
java复制private List<Movie> movies = new ArrayList<>(1000);
这样可以避免多次扩容(默认初始容量10,每次扩容增加50%)
4.2 批量操作优化
当需要导入大量电影时:
java复制// 低效做法
for(Movie m : externalList) {
system.addMovie(m); // 每次add都会检查重复ID
}
// 高效做法
List<Movie> temp = new ArrayList<>(externalList.size());
temp.addAll(externalList); // 批量添加
movies.addAll(temp); // 一次检查
5. 常见问题与解决方案
5.1 并发修改异常
当在遍历时修改集合会抛出ConcurrentModificationException:
java复制// 错误示例
for(Movie m : movies) {
if(m.getRating() < 3) {
movies.remove(m); // 抛出异常
}
}
// 正确做法1:使用迭代器
Iterator<Movie> it = movies.iterator();
while(it.hasNext()) {
if(it.next().getRating() < 3) {
it.remove();
}
}
// 正确做法2:使用removeIf
movies.removeIf(m -> m.getRating() < 3);
5.2 元素查找优化
当需要频繁按ID查找时,可以维护一个Map索引:
java复制private Map<String, Movie> movieMap = new HashMap<>();
public void addMovie(Movie movie) {
movies.add(movie);
movieMap.put(movie.getId(), movie); // 维护索引
}
public Movie getMovieById(String id) {
return movieMap.get(id); // O(1)时间复杂度
}
6. 功能扩展思路
6.1 支持分页查询
java复制public List<Movie> getMoviesByPage(int page, int size) {
int fromIndex = (page - 1) * size;
if(fromIndex >= movies.size()) return Collections.emptyList();
int toIndex = Math.min(fromIndex + size, movies.size());
return movies.subList(fromIndex, toIndex);
}
6.2 实现自定义排序
java复制// 按导演名字排序
movies.sort(Comparator.comparing(Movie::getDirector));
// 多条件排序:先按类型,再按评分
movies.sort(Comparator.comparing(Movie::getType)
.thenComparingDouble(Movie::getRating));
7. 测试与验证
编写单元测试验证核心功能:
java复制@Test
public void testAddMovie() {
MovieSystem system = new MovieSystem();
Movie m1 = new Movie("1", "肖申克的救赎", "弗兰克·德拉邦特",
Arrays.asList("蒂姆·罗宾斯", "摩根·弗里曼"), "剧情", 9.7);
system.addMovie(m1);
assertEquals(1, system.getAllMovies().size());
// 测试重复ID
Movie m2 = new Movie("1", "阿甘正传", "罗伯特·泽米吉斯",
Arrays.asList("汤姆·汉克斯"), "剧情", 9.5);
assertThrows(RuntimeException.class, () -> system.addMovie(m2));
}
8. 项目总结与反思
在实际开发这个案例时,有几个关键点值得注意:
-
equals和hashCode:如果要用contains等集合方法,必须为Movie类正确重写这两个方法
-
防御性拷贝:返回集合时应考虑返回不可修改视图或拷贝,避免外部修改破坏内部状态:
java复制public List<Movie> getAllMovies() {
return Collections.unmodifiableList(movies);
// 或者 return new ArrayList<>(movies);
}
- 容量监控:对于长期运行的系统,可以添加监控点检查ArrayList的size和实际容量比例,避免内存浪费
这个案例展示了如何用ArrayList构建一个完整的业务系统。虽然实际项目中可能会使用数据库,但理解这种基于内存的实现方式,对掌握集合框架的核心思想非常有帮助。当需要处理更复杂的关系时,可以考虑使用Map嵌套List等结构,如按导演分类:
java复制Map<String, List<Movie>> moviesByDirector = new HashMap<>();
