Skip to content

Commit 745909e

Browse files
committed
Experiment with a different approach
1 parent 29e6a54 commit 745909e

File tree

3 files changed

+281
-32
lines changed

3 files changed

+281
-32
lines changed

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractJoinNode.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -169,14 +169,7 @@ private void processOutTupleUpdate(LeftTuple_ leftTuple, UniTuple<Right_> rightT
169169
private static <Tuple_ extends AbstractTuple> @Nullable Tuple_ findOutTuple(IndexedSet<Tuple_> outTupleSet,
170170
IndexedSet<Tuple_> referenceOutTupleSet, int outputStoreIndexOutSet) {
171171
// Hack: the outTuple has no left/right input tuple reference, use the left/right outSet reference instead.
172-
var list = outTupleSet.asList();
173-
for (var i = 0; i < list.size(); i++) { // Avoid allocating iterators.
174-
var outTuple = list.get(i);
175-
if (referenceOutTupleSet == outTuple.getStore(outputStoreIndexOutSet)) {
176-
return outTuple;
177-
}
178-
}
179-
return null;
172+
return outTupleSet.findFirst(outTuple -> referenceOutTupleSet == outTuple.getStore(outputStoreIndexOutSet));
180173
}
181174

182175
protected final void retractOutTuple(OutTuple_ outTuple) {

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/index/IndexedSet.java

Lines changed: 60 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
package ai.timefold.solver.core.impl.bavet.common.index;
22

3-
import ai.timefold.solver.core.impl.util.ElementAwareList;
4-
import org.jspecify.annotations.NullMarked;
5-
import org.jspecify.annotations.Nullable;
6-
73
import java.util.ArrayList;
84
import java.util.BitSet;
95
import java.util.Collections;
106
import java.util.List;
117
import java.util.Objects;
128
import java.util.function.Consumer;
9+
import java.util.function.Predicate;
10+
11+
import ai.timefold.solver.core.impl.util.ElementAwareList;
12+
13+
import org.jspecify.annotations.NullMarked;
14+
import org.jspecify.annotations.Nullable;
1315

1416
/**
1517
* {@link ArrayList}-backed set which allows to {@link #remove(Object)} an element
@@ -72,16 +74,24 @@ public void add(T element) {
7274
var actualElementList = getElementList();
7375
if (gapCount > 0) {
7476
var gapIndex = gaps.nextSetBit(0);
75-
actualElementList.set(gapIndex, element);
76-
elementPositionTracker.setPosition(element, gapIndex);
77-
gaps.clear(gapIndex);
78-
gapCount--;
77+
putElementIntoGap(actualElementList, element, gapIndex);
7978
} else {
8079
actualElementList.add(element);
8180
elementPositionTracker.setPosition(element, actualElementList.size() - 1);
8281
}
8382
}
8483

84+
private void putElementIntoGap(List<T> elementList, T element, int gap) {
85+
setElementList(elementList, element, gap);
86+
gaps.clear(gap);
87+
gapCount--;
88+
}
89+
90+
private void setElementList(List<T> elementList, T element, int position) {
91+
elementList.set(position, element);
92+
elementPositionTracker.setPosition(element, position);
93+
}
94+
8595
/**
8696
* Removes the first occurrence of the specified element from this collection, if it is present.
8797
* Will use identity comparison to check for presence;
@@ -125,18 +135,37 @@ public int size() {
125135
* Performs the given action for each element of the collection
126136
* until all elements have been processed.
127137
*
128-
* @param tupleConsumer the action to be performed for each element
138+
* @param elementConsumer the action to be performed for each element
129139
*/
130-
public void forEach(Consumer<T> tupleConsumer) {
131-
if (elementList == null) {
140+
public void forEach(Consumer<T> elementConsumer) {
141+
if (isEmpty()) {
132142
return;
133143
}
144+
findFirst(element -> {
145+
elementConsumer.accept(element);
146+
return false; // Iterate until the end.
147+
});
148+
}
149+
150+
public @Nullable T findFirst(Predicate<T> elementPredicate) {
151+
if (isEmpty()) {
152+
return null;
153+
}
134154
for (var i = 0; i < elementList.size(); i++) {
135155
var element = elementList.get(i);
136-
if (element != null) {
137-
tupleConsumer.accept(element);
156+
if (element == null) {
157+
var nonGap = findNonGapFromEnd(elementList);
158+
if (i >= nonGap) {
159+
return null;
160+
}
161+
element = elementList.remove(nonGap);
162+
putElementIntoGap(elementList, element, i);
163+
}
164+
if (elementPredicate.test(element)) {
165+
return element;
138166
}
139167
}
168+
return null;
140169
}
141170

142171
public boolean isEmpty() {
@@ -153,9 +182,8 @@ public List<T> asList() {
153182
if (elementList == null) {
154183
return Collections.emptyList();
155184
}
156-
var actualElementList = getElementList();
157-
defrag(actualElementList);
158-
return actualElementList;
185+
defrag(elementList);
186+
return elementList;
159187
}
160188

161189
private void defrag(List<T> actualElementList) {
@@ -169,21 +197,32 @@ private void defrag(List<T> actualElementList) {
169197
break;
170198
}
171199
var lastElement = actualElementList.remove(lastNonGapIndex);
172-
actualElementList.set(gap, lastElement);
173-
elementPositionTracker.setPosition(lastElement, gap);
200+
setElementList(actualElementList, lastElement, gap);
174201
gap = gaps.nextSetBit(gap + 1);
175202
}
203+
resetGaps();
204+
}
205+
206+
private void resetGaps() {
176207
gaps.clear();
177208
gapCount = 0;
178209
}
179210

180211
private int findNonGapFromEnd(List<T> actualElementList) {
181212
var end = actualElementList.size() - 1;
182213
var lastNonGap = gaps.previousClearBit(end);
183-
for (var i = end; i > lastNonGap; i--) {
184-
actualElementList.remove(i);
214+
if (lastNonGap < 0) {
215+
actualElementList.clear();
216+
resetGaps();
217+
return -1;
218+
} else {
219+
for (var i = end; i > lastNonGap; i--) {
220+
actualElementList.remove(i);
221+
}
222+
gaps.clear(lastNonGap, end + 1);
223+
gapCount = gaps.cardinality();
224+
return lastNonGap;
185225
}
186-
return lastNonGap;
187226
}
188227

189228
}

0 commit comments

Comments
 (0)