You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OptaPlanner无法移除ConstraintMatch异常问题求助

Fixing java.lang.IllegalStateException When Removing ConstraintMatch in OptaPlanner

Let's break down your problem and walk through actionable fixes:

The Root Cause

You're hitting this exception because OptaPlanner's default behavior for Drools rules adds all objects matched in the when clause to the ConstraintMatch's justificationList. In your case, that includes the $shifts ArrayList (a mutable collection) and its contents. When this list or its elements change later, the hash code of the ConstraintMatch changes—so when the system tries to remove it from the ConstraintMatchSet (a hash-based collection), it can't find the original entry (since the hash no longer matches what was stored initially).

Solution 1: Customize ConstraintMatch Justification (Best Approach)

Instead of letting OptaPlanner auto-collect all matching objects as justification, explicitly pass only the stable, immutable objects you need. This prevents mutable collections/objects from messing with the ConstraintMatch's hash code.

Step 1: Modify your breakLevelConstraints method

Update the method to accept custom justification parameters, and use them when adding the constraint match to the ScoreHolder:

public void breakLevelConstraints(ScoreHolder scoreHolder, KieContext kcontext, 
                                  int constraintType, int penalty, Object... customJustifications) {
    String constraintKey = "com.k.sp.rules/Minimum Consecutive Nights";
    // Use custom justification instead of auto-collected objects
    scoreHolder.addHardConstraintMatch(kcontext, -penalty, customJustifications);
    // Swap to addMediumConstraintMatch or addSoftConstraintMatch if needed
}

Step 2: Update your Drools rule

Call the modified method with only stable objects (e.g., just the $emp instance, not the mutable $shifts list):

rule "Minimum Consecutive Nights" salience 20
when
$emp:Employee()
$shifts:ArrayList(size>0) from collect (ShiftRequestPhase(getInterval()!=null && employee.equals($emp)))
$constraintsPenality: Integer(intValue>0) from ($emp.checkConstraints($shifts,6))
then
// Pass only $emp as justification (add other stable objects if needed)
$emp.breakLevelConstraints(scoreHolder,kcontext,6,$constraintsPenality, $emp);
end

This way, the ConstraintMatch's justificationList only contains a stable object whose state won't change, so its hash code remains consistent.

Solution 2: Use Immutable Collections (Secondary Fix)

If you need to keep the collection in the justification, replace the mutable ArrayList with an immutable collection (like Guava's ImmutableList) to prevent changes to the collection itself:

import com.google.common.collect.ImmutableList;

rule "Minimum Consecutive Nights" salience 20
when
$emp:Employee()
// Collect into an immutable list instead of ArrayList
$shifts:ImmutableList(size>0) from collect (ShiftRequestPhase(getInterval()!=null && employee.equals($emp)))
$constraintsPenality: Integer(intValue>0) from ($emp.checkConstraints($shifts,6))
then
$emp.breakLevelConstraints(scoreHolder,kcontext,6,$constraintsPenality);
end

⚠️ Note: This only fixes changes to the collection itself. If the ShiftRequestPhase objects inside the list have mutable state, you'll still run into hash code issues—so combining this with Solution 1 is safer.

Why This Works

ConstraintMatch uses its constraintName and justificationList to compute hashCode() and equals(). By ensuring the justification only includes objects that don't change state, you guarantee the hash code stays the same from insertion to removal in the ConstraintMatchSet.

内容的提问来源于stack exchange,提问作者Sachin Verma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:55:09