@@ -58,13 +58,6 @@ public IndexedSet(ElementPositionTracker<T> elementPositionTracker) {
5858 this .elementPositionTracker = Objects .requireNonNull (elementPositionTracker );
5959 }
6060
61- private List <@ Nullable T > getElementList () {
62- if (elementList == null ) {
63- elementList = new ArrayList <>();
64- }
65- return elementList ;
66- }
67-
6861 /**
6962 * Appends the specified element to the end of this collection.
7063 * If the element is already present,
@@ -76,30 +69,26 @@ public IndexedSet(ElementPositionTracker<T> elementPositionTracker) {
7669 * @param element element to be appended to this collection
7770 */
7871 public void add (T element ) {
79- var actualElementList = getElementList ();
80- actualElementList .add (element );
72+ if (elementList == null ) {
73+ elementList = new ArrayList <>();
74+ }
75+ elementList .add (element );
8176 elementPositionTracker .setPosition (element , ++lastElementPosition );
8277 }
8378
8479 /**
85- * Removes the first occurrence of the specified element from this collection, if it is present.
80+ * Removes the first occurrence of the specified element from this collection, if present.
8681 * Will use identity comparison to check for presence;
8782 * two different instances which {@link Object#equals(Object) equal} are considered different elements.
8883 *
8984 * @param element element to be removed from this collection
90- * @throws IllegalStateException if the element was not found in this collection
85+ * @throws IllegalStateException if the element wasn't found in this collection
9186 */
9287 public void remove (T element ) {
93- if (!innerRemove (element )) {
94- throw new IllegalStateException ("Impossible state: the element (%s) was not found in the IndexedSet."
95- .formatted (element ));
96- }
97- }
98-
99- private boolean innerRemove (T element ) {
10088 var insertionPosition = elementPositionTracker .clearPosition (element );
10189 if (insertionPosition < 0 ) {
102- return false ;
90+ throw new IllegalStateException ("Impossible state: the element (%s) was not found in the IndexedSet."
91+ .formatted (element ));
10392 }
10493 if (insertionPosition == lastElementPosition ) {
10594 // The element was the last one added; we can simply remove it.
@@ -111,7 +100,6 @@ private boolean innerRemove(T element) {
111100 gapCount ++;
112101 }
113102 clearIfPossible ();
114- return true ;
115103 }
116104
117105 private boolean clearIfPossible () {
@@ -139,68 +127,65 @@ public int size() {
139127 *
140128 * @param elementConsumer the action to be performed for each element;
141129 * may include removing elements from the collection,
142- * but additions or swaps are not allowed;
130+ * but additions or swaps aren't allowed;
143131 * undefined behavior will occur if that is attempted.
144132 */
145133 public void forEach (Consumer <T > elementConsumer ) {
146134 if (isEmpty ()) {
147135 return ;
148136 }
149- forEach (element -> {
150- elementConsumer .accept (element );
151- return false ; // Iterate until the end.
152- });
153- }
154-
155- private @ Nullable T forEach (Predicate <T > elementPredicate ) {
156- return shouldCompact () ? forEachCompacting (elementPredicate ) : forEachNonCompacting (elementPredicate );
137+ if (shouldCompact ()) {
138+ forEachCompacting (elementConsumer );
139+ } else {
140+ forEachNonCompacting (elementConsumer );
141+ }
157142 }
158143
159144 private boolean shouldCompact () {
160- int elementCount = lastElementPosition + 1 ;
145+ if (gapCount == 0 ) {
146+ return false ;
147+ }
148+ var elementCount = lastElementPosition + 1 ;
161149 if (elementCount < MINIMUM_ELEMENT_COUNT_FOR_COMPACTION ) {
162150 return false ;
163151 }
164152 var gapPercentage = gapCount / (double ) elementCount ;
165153 return gapPercentage > GAP_RATIO_FOR_COMPACTION ;
166154 }
167155
168- private @ Nullable T forEachNonCompacting (Predicate <T > elementPredicate ) {
169- return forEachNonCompacting (elementPredicate , 0 );
156+ private void forEachNonCompacting (Consumer <T > elementConsumer ) {
157+ forEachNonCompacting (elementConsumer , 0 );
170158 }
171159
172- private @ Nullable T forEachNonCompacting (Predicate <T > elementPredicate , int startingIndex ) {
160+ private void forEachNonCompacting (Consumer <T > elementConsumer , int startingIndex ) {
173161 for (var i = startingIndex ; i <= lastElementPosition ; i ++) {
174162 var element = elementList .get (i );
175- if (element != null && elementPredicate . test ( element ) ) {
176- return element ;
163+ if (element != null ) {
164+ elementConsumer . accept ( element ) ;
177165 }
178166 }
179- return null ;
180167 }
181168
182- private @ Nullable T forEachCompacting (Predicate <T > elementPredicate ) {
169+ private void forEachCompacting (Consumer <T > elementConsumer ) {
183170 if (clearIfPossible ()) {
184- return null ;
171+ return ;
185172 }
186173 for (var i = 0 ; i <= lastElementPosition ; i ++) {
187174 var element = elementList .get (i );
188175 if (element == null ) {
189176 element = fillGap (i );
190177 if (element == null ) { // There was nothing to fill the gap with; we are done.
191- return null ;
178+ return ;
192179 }
193180 }
194- if (elementPredicate .test (element )) {
195- return element ;
196- }
181+ elementConsumer .accept (element );
197182 if (gapCount == 0 ) {
198183 // No more gaps to fill; we can continue without compacting.
199184 // This is an optimized loop which no longer checks for gaps.
200- return forEachNonCompacting (elementPredicate , i + 1 );
185+ forEachNonCompacting (elementConsumer , i + 1 );
186+ return ;
201187 }
202188 }
203- return null ;
204189 }
205190
206191 /**
@@ -210,7 +195,7 @@ private boolean shouldCompact() {
210195 * @return the element that now occupies position i, or null if no element further in the list can fill the gap
211196 */
212197 private @ Nullable T fillGap (int gapPosition ) {
213- T lastRemovedElement = removeLastNonGap (gapPosition );
198+ var lastRemovedElement = removeLastNonGap (gapPosition );
214199 if (lastRemovedElement == null ) {
215200 return null ;
216201 }
@@ -243,7 +228,45 @@ private boolean shouldCompact() {
243228 if (isEmpty ()) {
244229 return null ;
245230 }
246- return forEach (elementPredicate );
231+ return shouldCompact () ? findFirstCompacting (elementPredicate ) : findFirstNonCompacting (elementPredicate );
232+ }
233+
234+ private @ Nullable T findFirstNonCompacting (Predicate <T > elementPredicate ) {
235+ return findFirstNonCompacting (elementPredicate , 0 );
236+ }
237+
238+ private @ Nullable T findFirstNonCompacting (Predicate <T > elementPredicate , int startingIndex ) {
239+ for (var i = startingIndex ; i <= lastElementPosition ; i ++) {
240+ var element = elementList .get (i );
241+ if (element != null && elementPredicate .test (element )) {
242+ return element ;
243+ }
244+ }
245+ return null ;
246+ }
247+
248+ private @ Nullable T findFirstCompacting (Predicate <T > elementPredicate ) {
249+ if (clearIfPossible ()) {
250+ return null ;
251+ }
252+ for (var i = 0 ; i <= lastElementPosition ; i ++) {
253+ var element = elementList .get (i );
254+ if (element == null ) {
255+ element = fillGap (i );
256+ if (element == null ) { // There was nothing to fill the gap with; we are done.
257+ return null ;
258+ }
259+ }
260+ if (elementPredicate .test (element )) {
261+ return element ;
262+ }
263+ if (gapCount == 0 ) {
264+ // No more gaps to fill; we can continue without compacting.
265+ // This is an optimized loop which no longer checks for gaps.
266+ return findFirstNonCompacting (elementPredicate , i + 1 );
267+ }
268+ }
269+ return null ;
247270 }
248271
249272 /**
0 commit comments