Skip to content

Commit 2a7bca5

Browse files
GH-4847 ArrayBindingSetIterator should not advance on hasNext, also avoid LinkedHashSet althoughether
Signed-off-by: Jerven Bolleman <jerven.bolleman@sib.swiss>
1 parent 4d1e8d1 commit 2a7bca5

1 file changed

Lines changed: 65 additions & 38 deletions

File tree

  • core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation

core/queryalgebra/evaluation/src/main/java/org/eclipse/rdf4j/query/algebra/evaluation/ArrayBindingSet.java

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
*******************************************************************************/
1111
package org.eclipse.rdf4j.query.algebra.evaluation;
1212

13+
import java.util.AbstractSet;
1314
import java.util.ArrayList;
1415
import java.util.Arrays;
16+
import java.util.Collection;
1517
import java.util.Collections;
18+
import java.util.ConcurrentModificationException;
1619
import java.util.Iterator;
17-
import java.util.LinkedHashSet;
1820
import java.util.List;
1921
import java.util.Set;
2022
import java.util.function.BiConsumer;
@@ -44,9 +46,6 @@ public class ArrayBindingSet extends AbstractBindingSet implements MutableBindin
4446

4547
private final String[] bindingNames;
4648

47-
// Creating a LinkedHashSet is expensive, so we should cache the binding names set
48-
private Set<String> bindingNamesSetCache;
49-
5049
private final boolean[] whichBindingsHaveBeenSet;
5150

5251
private final Value[] values;
@@ -105,7 +104,6 @@ public BiConsumer<Value, ArrayBindingSet> getDirectSetBinding(String bindingName
105104
return (v, a) -> {
106105
a.values[index] = v;
107106
a.whichBindingsHaveBeenSet[index] = true;
108-
a.clearCache();
109107
};
110108
}
111109

@@ -120,7 +118,6 @@ public BiConsumer<Value, ArrayBindingSet> getDirectAddBinding(String bindingName
120118
assert !a.whichBindingsHaveBeenSet[index] : "variable already bound: " + bindingName;
121119
a.values[index] = v;
122120
a.whichBindingsHaveBeenSet[index] = true;
123-
a.clearCache();
124121
};
125122

126123
}
@@ -175,30 +172,21 @@ private int getIndex(String bindingName) {
175172

176173
@Override
177174
public Set<String> getBindingNames() {
178-
if (bindingNamesSetCache == null) {
179-
int size = size();
180-
if (size == 0) {
181-
this.bindingNamesSetCache = Collections.emptySet();
182-
} else if (size == 1) {
183-
for (int i = 0; i < this.bindingNames.length; i++) {
184-
if (whichBindingsHaveBeenSet[i]) {
185-
this.bindingNamesSetCache = Collections.singleton(bindingNames[i]);
186-
break;
187-
}
175+
final int size = ArrayBindingSet.this.size();
176+
switch (size) {
177+
case 0:
178+
return Collections.emptySet();
179+
case 1:
180+
for (int i = 0; i < this.bindingNames.length; i++) {
181+
if (whichBindingsHaveBeenSet[i]) {
182+
return Collections.singleton(bindingNames[i]);
188183
}
189-
assert this.bindingNamesSetCache != null;
190-
} else {
191-
LinkedHashSet<String> bindingNamesSetCache = new LinkedHashSet<>(size * 2);
192-
for (int i = 0; i < this.bindingNames.length; i++) {
193-
if (whichBindingsHaveBeenSet[i]) {
194-
bindingNamesSetCache.add(bindingNames[i]);
195-
}
196-
}
197-
this.bindingNamesSetCache = Collections.unmodifiableSet(bindingNamesSetCache);
198184
}
185+
throw new ConcurrentModificationException(
186+
"An bindingset has been modified during the getBindingNames call");
187+
default:
188+
return new MinimallyAllocatingSet(size);
199189
}
200-
201-
return bindingNamesSetCache;
202190
}
203191

204192
@Override
@@ -285,6 +273,53 @@ public List<String> getSortedBindingNames() {
285273
/*------------------------------------*
286274
* Inner class ArrayBindingSetIterator *
287275
*------------------------------------*/
276+
private static final class BindingToBindingNameIterator implements Iterator<String> {
277+
private final Iterator<Binding> nested;
278+
279+
private BindingToBindingNameIterator(Iterator<Binding> nested) {
280+
this.nested = nested;
281+
}
282+
283+
@Override
284+
public boolean hasNext() {
285+
return nested.hasNext();
286+
}
287+
288+
@Override
289+
public String next() {
290+
return nested.next().getName();
291+
}
292+
}
293+
294+
private final class MinimallyAllocatingSet extends AbstractSet<String> {
295+
296+
private final int size;
297+
298+
private MinimallyAllocatingSet(int size) {
299+
this.size = size;
300+
}
301+
302+
@Override
303+
public int size() {
304+
return size;
305+
}
306+
307+
@Override
308+
public Iterator<String> iterator() {
309+
Iterator<Binding> nested = ArrayBindingSet.this.iterator();
310+
return new BindingToBindingNameIterator(nested);
311+
}
312+
313+
@Override
314+
public boolean add(String e) {
315+
throw new UnsupportedOperationException();
316+
}
317+
318+
@Override
319+
public boolean addAll(Collection<? extends String> c) {
320+
throw new UnsupportedOperationException();
321+
}
322+
}
288323

289324
private class ArrayBindingSetIterator implements Iterator<Binding> {
290325

@@ -295,8 +330,8 @@ public ArrayBindingSetIterator() {
295330

296331
@Override
297332
public boolean hasNext() {
298-
for (; index < values.length; index++) {
299-
if (whichBindingsHaveBeenSet[index] && values[index] != null) {
333+
for (int at = index; at < values.length; at++) {
334+
if (whichBindingsHaveBeenSet[at] && values[at] != null) {
300335
return true;
301336
}
302337
}
@@ -337,11 +372,9 @@ public void addBinding(Binding binding) {
337372
return;
338373
}
339374

340-
assert !this.whichBindingsHaveBeenSet[index];
375+
assert !this.whichBindingsHaveBeenSet[index] : "variable already bound: " + binding.getName();
341376
this.values[index] = binding.getValue();
342377
this.whichBindingsHaveBeenSet[index] = true;
343-
clearCache();
344-
345378
}
346379

347380
@Override
@@ -352,7 +385,6 @@ public void setBinding(Binding binding) {
352385
}
353386
this.values[index] = binding.getValue();
354387
this.whichBindingsHaveBeenSet[index] = true;
355-
clearCache();
356388
}
357389

358390
@Override
@@ -364,7 +396,6 @@ public void setBinding(String name, Value value) {
364396

365397
this.values[index] = value;
366398
this.whichBindingsHaveBeenSet[index] = value != null;
367-
clearCache();
368399
}
369400

370401
@Override
@@ -376,8 +407,4 @@ public boolean isEmpty() {
376407
}
377408
return true;
378409
}
379-
380-
private void clearCache() {
381-
bindingNamesSetCache = null;
382-
}
383410
}

0 commit comments

Comments
 (0)