1. 项目概述:用ArrayList构建电影管理系统
去年接手一个电影票务系统改造项目时,我惊讶地发现核心模块竟然是用最基础的ArrayList实现的。这个看似简单的数据结构,配合面向对象设计理念,完全能够支撑起完整的电影管理功能。今天我们就来拆解如何用ArrayList打造一个五脏俱全的电影管理系统。
这个案例特别适合刚学完Java集合框架的开发者练手,它涵盖了:
- ArrayList的CRUD操作(增删改查)
- 对象间关联关系的设计
- 业务逻辑与数据结构的结合
- 控制台交互的简易实现
提示:虽然实际商业项目会用数据库+框架,但理解这种基础实现对掌握编程本质至关重要
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心类设计
2.1 电影实体类建模
首先定义Movie类,包含电影基本属性。注意这里用枚举定义电影类型比直接用字符串更规范:
java复制public enum MovieType {
ACTION, COMEDY, ROMANCE, SCI_FI, HORROR
}
public class Movie {
private int id; // 电影ID
private String name; // 电影名称
private MovieType type; // 电影类型
private int duration; // 片长(分钟)
private double score; // 评分(0-10)
// 构造方法、getter/setter省略...
}
2.2 管理系统类设计
MovieSystem类作为核心控制器,使用ArrayList存储电影数据:
java复制public class MovieSystem {
private List<Movie> movies = new ArrayList<>();
private int nextId = 1; // 自增ID
// 核心操作方法见下文...
}
这里选择ArrayList而非LinkedList的考虑:
- 电影数据更多是随机访问而非频繁首尾操作
- 内存占用更小(每个节点节省8字节指针空间)
- 遍历效率更高(CPU缓存友好)
3. 核心功能实现
3.1 新增电影
java复制public void addMovie(String name, MovieType type, int duration) {
Movie movie = new Movie();
movie.setId(nextId++);
movie.setName(name);
movie.setType(type);
movie.setDuration(duration);
movie.setScore(5.0); // 默认5分
movies.add(movie);
System.out.println("添加成功!ID:" + movie.getId());
}
注意:实际项目要考虑线程安全问题,这里简化处理
3.2 查询电影
实现按不同条件查询:
java复制// 按ID查询
public Movie getById(int id) {
for (Movie m : movies) {
if (m.getId() == id) {
return m;
}
}
return null;
}
// 按名称模糊查询
public List<Movie> searchByName(String keyword) {
List<Movie> result = new ArrayList<>();
for (Movie m : movies) {
if (m.getName().contains(keyword)) {
result.add(m);
}
}
return result;
}
// 按类型查询(Java8流式写法)
public List<Movie> getByType(MovieType type) {
return movies.stream()
.filter(m -> m.getType() == type)
.collect(Collectors.toList());
}
3.3 修改电影信息
java复制public boolean updateMovie(int id, String newName, MovieType newType) {
Movie movie = getById(id);
if (movie != null) {
movie.setName(newName);
movie.setType(newType);
return true;
}
return false;
}
3.4 删除电影
java复制public boolean deleteMovie(int id) {
Iterator<Movie> it = movies.iterator();
while (it.hasNext()) {
if (it.next().getId() == id) {
it.remove();
return true;
}
}
return false;
}
踩坑提醒:不要在for循环中直接调用remove,要用Iterator删除
4. 扩展功能实现
4.1 电影评分功能
java复制public boolean rateMovie(int id, double score) {
if (score < 0 || score > 10) {
return false;
}
Movie movie = getById(id);
if (movie != null) {
// 简单平均分算法
double newScore = (movie.getScore() + score) / 2;
movie.setScore(newScore);
return true;
}
return false;
}
4.2 电影排序功能
java复制// 按评分降序
public void sortByScore() {
movies.sort((m1, m2) ->
Double.compare(m2.getScore(), m1.getScore()));
}
// 按片长升序
public void sortByDuration() {
movies.sort(Comparator.comparingInt(Movie::getDuration));
}
4.3 数据持久化(简易版)
java复制// 保存到文件
public void saveToFile(String filename) throws IOException {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(movies);
}
}
// 从文件加载
@SuppressWarnings("unchecked")
public void loadFromFile(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filename))) {
movies = (List<Movie>) ois.readObject();
// 更新nextId
nextId = movies.stream()
.mapToInt(Movie::getId)
.max().orElse(0) + 1;
}
}
5. 控制台交互实现
5.1 主菜单设计
java复制public void showMenu() {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("\n=== 电影管理系统 ===");
System.out.println("1. 添加电影");
System.out.println("2. 查询电影");
System.out.println("3. 修改电影");
System.out.println("4. 删除电影");
System.out.println("5. 电影评分");
System.out.println("6. 电影排序");
System.out.println("0. 退出系统");
System.out.print("请选择:");
int choice = scanner.nextInt();
scanner.nextLine(); // 消耗换行符
switch (choice) {
case 1 -> addMovieUI(scanner);
case 2 -> searchMovieUI(scanner);
// 其他case处理...
case 0 -> {
System.out.println("谢谢使用!");
return;
}
default -> System.out.println("无效输入!");
}
}
}
5.2 添加电影界面
java复制private void addMovieUI(Scanner scanner) {
System.out.print("输入电影名称:");
String name = scanner.nextLine();
System.out.println("选择电影类型:");
for (MovieType type : MovieType.values()) {
System.out.println(type.ordinal() + ". " + type.name());
}
int typeIdx = scanner.nextInt();
MovieType type = MovieType.values()[typeIdx];
System.out.print("输入片长(分钟):");
int duration = scanner.nextInt();
addMovie(name, type, duration);
}
6. 性能优化与注意事项
6.1 查询优化方案
当电影数量超过1000部时,线性查找效率会明显下降。可以考虑:
- 维护一个ID到对象的HashMap作为索引:
java复制private Map<Integer, Movie> idIndex = new HashMap<>();
// 添加电影时同步更新索引
public void addMovie(...) {
// ...原有代码...
idIndex.put(movie.getId(), movie);
}
- 对名称建立倒排索引:
java复制private Map<String, List<Movie>> nameIndex = new HashMap<>();
// 添加电影时更新索引
public void addMovie(...) {
// ...原有代码...
nameIndex.computeIfAbsent(movie.getName(), k -> new ArrayList<>())
.add(movie);
}
6.2 内存管理要点
- 大数据量时考虑分页加载:
java复制public List<Movie> getMovies(int page, int pageSize) {
int from = page * pageSize;
int to = Math.min(from + pageSize, movies.size());
return new ArrayList<>(movies.subList(from, to));
}
- 定期清理无效数据:
java复制public void cleanupMovies() {
movies.removeIf(movie ->
movie.getScore() < 2 &&
LocalDate.now().getYear() - movie.getYear() > 5);
}
7. 常见问题排查
7.1 ConcurrentModificationException
在遍历集合时修改会抛出此异常。解决方案:
- 使用Iterator的remove方法
- 使用Java8的removeIf
- 先收集要删除的元素,最后统一删除
7.2 对象序列化问题
如果Movie类后续增加了字段,可能导致反序列化失败。建议:
- 添加serialVersionUID:
java复制private static final long serialVersionUID = 1L;
- 考虑改用JSON格式存储
7.3 空指针防护
所有公开方法都应进行参数校验:
java复制public void addMovie(String name, MovieType type, int duration) {
if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("电影名不能为空");
}
if (type == null) {
throw new IllegalArgumentException("必须指定电影类型");
}
// ...原有代码...
}
8. 项目扩展方向
8.1 引入用户系统
- 创建User类
- 实现用户收藏功能:
java复制public class User {
private List<Movie> favorites = new ArrayList<>();
public void addFavorite(Movie movie) {
if (!favorites.contains(movie)) {
favorites.add(movie);
}
}
}
8.2 实现推荐算法
基于用户收藏的简单推荐:
java复制public List<Movie> recommendMovies(User user) {
return movies.stream()
.filter(m -> user.getFavorites().stream()
.anyMatch(f -> f.getType() == m.getType()))
.sorted(Comparator.comparingDouble(Movie::getScore).reversed())
.limit(5)
.collect(Collectors.toList());
}
8.3 接入真实数据源
可以扩展从豆瓣API获取数据:
java复制public void importFromDouban(String keyword) {
// 使用HttpClient调用豆瓣API
// 解析JSON响应
// 转换为Movie对象并添加
}
