应使用HashMap管理课程与学生映射,指定初始容量;用computeIfAbsent安全添加学生;查重选课用HashSet(需正确重写equals/hashCode);排序推荐用TreeSet配自定义Comparator;按时间序展示用LinkedHashMap(插入序)。

如何用 HashMap 管理课程与学生映射关系
学生选课本质是“一对多”关系:一门课对应多个学生,一个学生可选多门课。直接用 HashMap 最直观——课程编号作 key,学生列表作 value。
注意别用 ArrayList 存所有选课记录再遍历查找,性能差(O(n));也别把学生 ID 拼成字符串存进 map,丧失对象语义和类型安全。
-
HashMap初始化时建议指定初始容量,比如预估 50 门课,就写new HashMap(64),避免频繁扩容 - 添加选课时,先
computeIfAbsent获取或创建对应课程的ArrayList,再add学生对象,避免空指针 - 不要在遍历
values()时直接修改某个List,会触发ConcurrentModificationException;如需动态删选课,改用Iterator或收集待删项后批量处理
为什么 HashSet 比 ArrayList 更适合查重选课
学生不能重复选择同一门课,校验逻辑必须快且可靠。用 ArrayList 存已选课号,每次 contains() 是 O(n);换成 HashSet,平均 O(1),且自动去重。
关键前提是:Student 类必须正确重写 hashCode() 和 equals(),否则用 HashSet 判断“同一学生”会失效。
立即学习“Java免费学习笔记(深入)”;
- 如果用学号
id唯一标识学生,equals()只比较id字段,hashCode()基于id计算 - 别在
HashSet里存可变对象(比如未设 final 的字段),一旦修改影响 hash 值,对象可能“丢失”——再也contains()不到 - 调试时发现
set.contains(x)返回 false,先打印x.hashCode()和集合中同类对象的 hash 值,确认是否重写有误
用 TreeSet 实现按学分排序的课程推荐
系统要给学生推荐“学分从高到低”的可选课程,需要天然有序的集合。TreeSet 能自动排序,但必须明确告诉它怎么比。
别直接让 Course 实现 Comparable 并固定按学分排——这样后续想按名称或开课时间排就冲突了。更灵活的做法是传入自定义 Comparator:
new TreeSet<>((a, b) -> Integer.compare(b.getCredit(), a.getCredit()))
这样既复用 TreeSet 的 O(log n) 插入/查询,又不污染业务类。
-
TreeSet不允许null元素,插入前确保Course对象非空 - 如果推荐逻辑需支持多种排序策略(如“学分优先,相同学分按名称升序”),用链式比较:
Comparator.comparing(Course::getCredit).reversed().thenComparing(Course::getName) - 注意
TreeSet的subSet、headSet等方法返回的是原 set 的视图,修改会影响原集合
什么时候该用 LinkedHashMap 记录选课时间顺序
学生选课有先后,系统需展示“最近选的 5 门课”。HashMap 不保证顺序,TreeMap 按 key 排序(不是时间),这时 LinkedHashMap 是唯一轻量解法——它按插入顺序迭代。
但要注意:默认构造的 LinkedHashMap 是插入序;若传入 true 构造参数(即 new LinkedHashMap(16, 0.75f, true)),则变成访问序(LRU 缓存逻辑),不适合此处。
- 要限制只存最近 N 条,得手动在
put后检查 size,超限时调用removeFirstEntry()(Java 21+)或用迭代器删头节点 - 别把时间戳当 key 存进
LinkedHashMap——时间可能重复,导致覆盖;应把Course对象作 value,key 仍用课程 ID,靠链表维护顺序 - 序列化
LinkedHashMap时顺序保留,但反序列化后仍是插入序,这点比TreeMap更可控
Student 和 Course 的 equals/hashCode 实现细节,以及 LinkedHashMap 构造参数里那个布尔值的意义——错一位,整个顺序逻辑就崩了。










