11package 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-
73import java .util .ArrayList ;
84import java .util .BitSet ;
95import java .util .Collections ;
106import java .util .List ;
117import java .util .Objects ;
128import 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