Optaplanner:定制起诉列表显示项及ProblemFact引用问题
OptaPlanner 约束起诉列表优化与ProblemFact引用问题解决方案
一、让起诉列表仅显示Student对象
你当前使用groupBy得到的BiConstraintStream<Student, List<Participation>>,默认会把流中的两个元素(Student和Participation列表)都作为约束违规的justification,所以起诉列表里会同时出现两者。
要解决这个问题,直接用OptaPlanner提供的justifyWith方法,显式指定只将Student作为约束违规的justification对象即可。修改后的约束代码如下:
return constraintFactory .forEach(Participation.class) .groupBy(Participation::getStudent, toList()) .filter((student, participations) -> student.getMinEnrollments() != null) .justifyWith((student, participations) -> student) // 仅指定Student作为justification .penalize("Not enough hours enrolled", HardSoftScore.ONE_SOFT, (student, participationList) -> student.getMinEnrollments() - participationList.stream().filter(Participation::getEnrolled).count() );
修改后,起诉列表里就只会显示对应的Student对象,不会再出现Participation列表或数值。
二、ProblemFact引用PlanningEntity的正确方式
你遇到的引用失效问题,本质是OptaPlanner的克隆机制导致的:PlanningSolution会被深度克隆生成不同候选解,其中ProblemFact(如Student)是只读的,仅克隆一次,而PlanningEntity(如Participation)会被多次克隆,每个候选解对应独立的Entity实例。直接在Student中持有Participation的初始引用,会导致约束计算时引用的是原始Entity,而非当前候选解中的实例,状态不匹配。
正确做法是通过约束流的关联操作(如join)建立Student和Participation的关联,而非直接持有引用。示例代码如下:
return constraintFactory .forEach(Student.class) .filter(student -> student.getMinEnrollments() != null) // 通过ID关联Student和对应的Participation .join(Participation.class, Joiners.equal(Student::getId, Participation::getStudentId)) // 按Student分组,统计已选课的数量 .groupBy((student, participation) -> student, count(Participation::getEnrolled)) .justifyWith((student, enrolledCount) -> student) // 指定justification为Student .penalize("Not enough hours enrolled", HardSoftScore.ONE_SOFT, (student, enrolledCount) -> student.getMinEnrollments() - enrolledCount );
这种方式既保证了约束计算能获取当前候选解中Participation的真实状态,也能通过justifyWith控制起诉列表的显示内容。
内容的提问来源于stack exchange,提问作者Radical
相关产品推荐
相关产品推荐

