Created
February 8, 2020 09:36
-
-
Save vladimir-bukhtoyarov/b2db7d093b4c0fc7c05675cbd1ec556a to your computer and use it in GitHub Desktop.
Concurrent array which expands its size if necessary
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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