学生选课系统需用TreeSet按courseCode排序去重,ConcurrentHashMap >保障高并发安全,校验逻辑须前置分离,一致性责任应明确分层。

学生选课系统不是集合的练习题,而是对关系建模、状态约束和并发边界的真实检验。用 ArrayList 存课程、HashMap 存学生选课记录,跑得通但撑不住一次期初抢课。
用 Set 管理课程唯一性,但别只靠 HashSet
课程编号(如 "CS201")必须全局唯一,但仅靠 HashSet 容易翻车:如果没重写 Course.equals() 和 Course.hashCode(),两个字段完全相同的 Course 实例会被当作不同对象存入。
- 必须确保
Course类中equals()基于courseCode判断,且hashCode()仅由courseCode计算 - 更稳妥的做法是直接用
TreeSet并实现Comparable,按courseCode排序,天然去重+可查范围 - 避免把整个
Course对象当 key 放进HashMap——一旦后续修改了影响hashCode()的字段(比如动态改了学分),该对象就再也找不到了
Map> 表示选课关系?小心并发修改异常
常见设计是用 Map 表示 “学生ID → 课程ID集合”,但 HashMap + HashSet 组合在多线程选课时会抛 ConcurrentModificationException,哪怕只是两个学生同时选同一门课。
- 不要手动加
synchronized块包裹整个 map 操作——吞吐量暴跌,抢课变成排队 - 改用
ConcurrentHashMap,它支持高并发下的安全添加/删除,且> ConcurrentSkipListSet本身线程安全 - 注意:
ConcurrentHashMap.computeIfAbsent()是原子操作,适合懒初始化学生课程集:courseSet = studentCourseMap.computeIfAbsent(studentId, k -> new ConcurrentSkipListSet<>());
选课前的校验逻辑不能塞在集合操作里
集合只负责“存取”,不该承担“能否选”的业务判断。比如“同一学期不能选两门实验时间冲突的课”,这种规则硬编码进 add() 方法会导致集合失去通用性,也难以测试。
立即学习“Java免费学习笔记(深入)”;
- 校验应前置:调用
enrollStudent(String studentId, String courseId)之前,先走独立的ScheduleConflictChecker.check(studentId, courseId) - 冲突检查依赖多个数据源:学生已选课表、课程时间表、教室占用表——这些不该全塞进一个集合结构,而应通过 DAO 分层获取
- 若用
LinkedHashSet保持选课顺序,只为展示“最近选的课排最前”,那纯属视图需求,和业务规则无关,别让它干扰核心逻辑
真正卡住系统的从来不是集合类型选错,而是没想清楚“谁负责一致性”——是数据库约束?服务层校验?还是最终靠分布式锁兜底?集合只是工具,它不会替你做决策。










