diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java index bac4bd4b2b..9e9b87f265 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java @@ -8,7 +8,6 @@ import ai.timefold.solver.core.impl.domain.variable.nextprev.PreviousElementShadowVariableDescriptor; import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; import ai.timefold.solver.core.preview.api.domain.metamodel.ElementLocation; -import ai.timefold.solver.core.preview.api.domain.metamodel.LocationInList; import org.jspecify.annotations.NonNull; @@ -129,11 +128,6 @@ public ElementLocation getLocationInList(Object planningValue) { return listVariableState.getLocationInList(planningValue); } - @Override - public LocationInList getNextLocationInList(Object planningValue) { - return listVariableState.getNextLocationInList(planningValue); - } - @Override public Integer getIndex(Object planningValue) { return listVariableState.getIndex(planningValue); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java index 0311e60b52..fa3687e085 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java @@ -222,24 +222,6 @@ public ElementLocation getLocationInList(Object planningValue) { } } - public LocationInList getNextLocationInList(Object planningValue) { - if (requiresLocationMap) { - var mutableLocationInList = elementLocationMap.get(planningValue); - if (mutableLocationInList == null) { - throw new IllegalArgumentException("The element (%s) is not assigned (%s)." - .formatted(planningValue, sourceVariableDescriptor)); - } - return ElementLocation.of(mutableLocationInList.getEntity(), mutableLocationInList.getIndex() + 1); - } else { // At this point, both inverse and index are externalized. - var inverse = externalizedInverseProcessor.getInverseSingleton(planningValue); - if (inverse == null) { - throw new IllegalArgumentException("The element (%s) is not assigned (%s)." - .formatted(planningValue, sourceVariableDescriptor)); - } - return ElementLocation.of(inverse, externalizedIndexProcessor.getIndex(planningValue) + 1); - } - } - public Integer getIndex(Object planningValue) { if (externalizedIndexProcessor == null) { var elementLocation = elementLocationMap.get(planningValue); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableStateSupply.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableStateSupply.java index 50b13dad57..7292b303ed 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableStateSupply.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableStateSupply.java @@ -11,7 +11,6 @@ import ai.timefold.solver.core.impl.domain.variable.nextprev.NextElementShadowVariableDescriptor; import ai.timefold.solver.core.impl.domain.variable.nextprev.PreviousElementShadowVariableDescriptor; import ai.timefold.solver.core.preview.api.domain.metamodel.ElementLocation; -import ai.timefold.solver.core.preview.api.domain.metamodel.LocationInList; /** * Single source of truth for all information about elements inside {@link PlanningListVariable list variables}. @@ -67,18 +66,6 @@ public interface ListVariableStateSupply extends */ ElementLocation getLocationInList(Object value); - /** - * Serves as a short-hand for {@link #getLocationInList(Object)} + 1. - * This way, we can only allocate the location instance for the next element, - * whereas {@link #getLocationInList(Object)} would allocate the location instance for the current element as well. - * This is a micro-optimization, but this code lives on the hot path. - * - * @param value never null - * @return never null - * @throws IllegalArgumentException if the value is not in the list - */ - LocationInList getNextLocationInList(Object value); - /** * Consider calling this before {@link #isAssigned(Object)} to eliminate some map accesses. * If unassigned count is 0, {@link #isAssigned(Object)} is guaranteed to return true. diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelector.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelector.java index 7204588b4f..4b21fe7dc2 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelector.java @@ -129,7 +129,8 @@ public Iterator iterator() { // Simplify tests. stream = Stream.concat(stream, StreamSupport.stream(valueSelector.spliterator(), false) - .map(v -> listVariableStateSupply.getNextLocationInList(v))); + .map(v -> listVariableStateSupply.getLocationInList(v).ensureAssigned()) + .map(locationInList -> ElementLocation.of(locationInList.entity(), locationInList.index() + 1))); // If the list variable allows unassigned values, add the option of unassigning. if (listVariableDescriptor.allowsUnassignedValues()) { stream = Stream.concat(stream, Stream.of(ElementLocation.unassigned())); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementLocationRandomIterator.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementLocationRandomIterator.java index 9be0f338f1..d340eff2bb 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementLocationRandomIterator.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementLocationRandomIterator.java @@ -84,8 +84,10 @@ public ElementLocation next() { var randomIndex = workingRandom.nextInt(unpinnedSize + 1); return ElementLocation.of(entity, listVariableDescriptor.getFirstUnpinnedIndex(entity) + randomIndex); } - } else { // Include the destination after the final element in the list. - return listVariableStateSupply.getNextLocationInList(value); + } else { // +1 to include the destination after the final element in the list. + var elementLocation = listVariableStateSupply.getLocationInList(value) + .ensureAssigned(); + return ElementLocation.of(elementLocation.entity(), elementLocation.index() + 1); } } }