Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save vladimir-bukhtoyarov/b2db7d093b4c0fc7c05675cbd1ec556a to your computer and use it in GitHub Desktop.
Save vladimir-bukhtoyarov/b2db7d093b4c0fc7c05675cbd1ec556a to your computer and use it in GitHub Desktop.
Concurrent array which expands its size if necessary
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class AutoExpandingConcurrentArray<T> {
private static int CHUNK_SIZE = 1024;
private static int MODULE_MASK = 1023;
private static int SHR = 9;
private final AtomicReference<List<AtomicReferenceArray<T>>> arraysRef = initial();
private final Queue<AtomicReferenceArray<T>> failedCasArrays = new ConcurrentLinkedQueue<>();
private final AtomicInteger size = new AtomicInteger();
private AtomicReference<List<AtomicReferenceArray<T>>> initial() {
List<AtomicReferenceArray<T>> arraysOfArray = new ArrayList<>();
arraysOfArray.add(new AtomicReferenceArray<>(CHUNK_SIZE));
return new AtomicReference<>(arraysOfArray);
}
/**
* Appends the specified element to the end of this array, the size of array will be expanded if necessary
*
* @param element element to add
*
* @return index of element which can be used for {@link #get(int)}
*/
public int add(T element) {
int idx = size.incrementAndGet() - 1;
while (true) {
List<AtomicReferenceArray<T>> arrayOfArrays = arraysRef.get();
int chunkIndex = idx >> SHR;
if (chunkIndex < arrayOfArrays.size()) {
AtomicReferenceArray<T> array = arrayOfArrays.get(chunkIndex);
array.set(idx & MODULE_MASK, element);
return idx;
}
AtomicReferenceArray<T> array = failedCasArrays.poll();
if (array == null) {
array = new AtomicReferenceArray<>(CHUNK_SIZE);
}
List<AtomicReferenceArray<T>> newArrayOfArrays = new ArrayList<>(arrayOfArrays);
newArrayOfArrays.add(array);
if (arraysRef.compareAndSet(arrayOfArrays, newArrayOfArrays)) {
array.set(idx & MODULE_MASK, element);
return idx;
} else {
failedCasArrays.add(array);
continue;
}
}
}
/**
* Returns the element at the specified position in this array.
*
* @param index index of the element to return
*
* @return the element at the specified position in this list
*/
public T get(int index) {
checkBounds(index);
int chunkIndex = index >> SHR;
List<AtomicReferenceArray<T>> arrayOfArrays = arraysRef.get();
AtomicReferenceArray<T> array = arrayOfArrays.get(chunkIndex);
return array.get(index & MODULE_MASK);
}
/**
* Writes the new value at the specified position in this array.
*
* @param index index of the element to forget
*/
public void set(int index, T value) {
checkBounds(index);
int chunkIndex = index >> SHR;
List<AtomicReferenceArray<T>> arrayOfArrays = arraysRef.get();
AtomicReferenceArray<T> array = arrayOfArrays.get(chunkIndex);
array.set(index & MODULE_MASK, value);
}
private void checkBounds(int index) {
if (index < 0 || index >= size.get()) {
throw new ArrayIndexOutOfBoundsException(index);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment