Sync BitList for all the operations (#14057)

* Sync BitList for all the operations

* Fix style
This commit is contained in:
Albumen Kevin 2024-05-06 14:33:10 +08:00 committed by GitHub
parent 20f252d147
commit d16f0ca310
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 79 additions and 37 deletions

View File

@ -93,23 +93,23 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
// Provided by BitList only // Provided by BitList only
public List<E> getOriginList() { public synchronized List<E> getOriginList() {
return originList; return originList;
} }
public void addIndex(int index) { public synchronized void addIndex(int index) {
this.rootSet.set(index); this.rootSet.set(index);
} }
public int totalSetSize() { public synchronized int totalSetSize() {
return this.originList.size(); return this.originList.size();
} }
public boolean indexExist(int index) { public synchronized boolean indexExist(int index) {
return this.rootSet.get(index); return this.rootSet.get(index);
} }
public E getByIndex(int index) { public synchronized E getByIndex(int index) {
return this.originList.get(index); return this.originList.get(index);
} }
@ -120,7 +120,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
* @param target target bitList * @param target target bitList
* @return this bitList only contains those elements contain in both two list and source bitList's tailList * @return this bitList only contains those elements contain in both two list and source bitList's tailList
*/ */
public BitList<E> and(BitList<E> target) { public synchronized BitList<E> and(BitList<E> target) {
rootSet.and(target.rootSet); rootSet.and(target.rootSet);
if (target.getTailList() != null) { if (target.getTailList() != null) {
target.getTailList().forEach(this::addToTailList); target.getTailList().forEach(this::addToTailList);
@ -128,28 +128,28 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
return this; return this;
} }
public BitList<E> or(BitList<E> target) { public synchronized BitList<E> or(BitList<E> target) {
BitSet resultSet = (BitSet) rootSet.clone(); BitSet resultSet = (BitSet) rootSet.clone();
resultSet.or(target.rootSet); resultSet.or(target.rootSet);
return new BitList<>(originList, resultSet, tailList); return new BitList<>(originList, resultSet, tailList);
} }
public boolean hasMoreElementInTailList() { public synchronized boolean hasMoreElementInTailList() {
return CollectionUtils.isNotEmpty(tailList); return CollectionUtils.isNotEmpty(tailList);
} }
public List<E> getTailList() { public synchronized List<E> getTailList() {
return tailList; return tailList;
} }
public void addToTailList(E e) { public synchronized void addToTailList(E e) {
if (tailList == null) { if (tailList == null) {
tailList = new LinkedList<>(); tailList = new LinkedList<>();
} }
tailList.add(e); tailList.add(e);
} }
public E randomSelectOne() { public synchronized E randomSelectOne() {
int originSize = originList.size(); int originSize = originList.size();
int tailSize = tailList != null ? tailList.size() : 0; int tailSize = tailList != null ? tailList.size() : 0;
int totalSize = originSize + tailSize; int totalSize = originSize + tailSize;
@ -181,18 +181,18 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
// Provided by JDK List interface // Provided by JDK List interface
@Override @Override
public int size() { public synchronized int size() {
return rootSet.cardinality() + (CollectionUtils.isNotEmpty(tailList) ? tailList.size() : 0); return rootSet.cardinality() + (CollectionUtils.isNotEmpty(tailList) ? tailList.size() : 0);
} }
@Override @Override
public boolean contains(Object o) { public synchronized boolean contains(Object o) {
int idx = originList.indexOf(o); int idx = originList.indexOf(o);
return (idx >= 0 && rootSet.get(idx)) || (CollectionUtils.isNotEmpty(tailList) && tailList.contains(o)); return (idx >= 0 && rootSet.get(idx)) || (CollectionUtils.isNotEmpty(tailList) && tailList.contains(o));
} }
@Override @Override
public Iterator<E> iterator() { public synchronized Iterator<E> iterator() {
return new BitListIterator<>(this, 0); return new BitListIterator<>(this, 0);
} }
@ -205,7 +205,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
* Notice: It is not recommended adding duplicated element. * Notice: It is not recommended adding duplicated element.
*/ */
@Override @Override
public boolean add(E e) { public synchronized boolean add(E e) {
int index = originList.indexOf(e); int index = originList.indexOf(e);
if (index > -1) { if (index > -1) {
rootSet.set(index); rootSet.set(index);
@ -225,7 +225,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
* If the element is not contained in originList, try to remove from tailList. * If the element is not contained in originList, try to remove from tailList.
*/ */
@Override @Override
public boolean remove(Object o) { public synchronized boolean remove(Object o) {
int idx = originList.indexOf(o); int idx = originList.indexOf(o);
if (idx > -1 && rootSet.get(idx)) { if (idx > -1 && rootSet.get(idx)) {
rootSet.set(idx, false); rootSet.set(idx, false);
@ -242,7 +242,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
* This may change the default behaviour when adding new element later. * This may change the default behaviour when adding new element later.
*/ */
@Override @Override
public void clear() { public synchronized void clear() {
rootSet.clear(); rootSet.clear();
// to remove references // to remove references
originList = Collections.emptyList(); originList = Collections.emptyList();
@ -252,7 +252,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public E get(int index) { public synchronized E get(int index) {
int bitIndex = -1; int bitIndex = -1;
if (index < 0) { if (index < 0) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
@ -272,7 +272,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public E remove(int index) { public synchronized E remove(int index) {
int bitIndex = -1; int bitIndex = -1;
if (index >= rootSet.cardinality()) { if (index >= rootSet.cardinality()) {
if (CollectionUtils.isNotEmpty(tailList)) { if (CollectionUtils.isNotEmpty(tailList)) {
@ -290,7 +290,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public int indexOf(Object o) { public synchronized int indexOf(Object o) {
int bitIndex = -1; int bitIndex = -1;
for (int i = 0; i < rootSet.cardinality(); i++) { for (int i = 0; i < rootSet.cardinality(); i++) {
bitIndex = rootSet.nextSetBit(bitIndex + 1); bitIndex = rootSet.nextSetBit(bitIndex + 1);
@ -311,7 +311,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public boolean addAll(Collection<? extends E> c) { public synchronized boolean addAll(Collection<? extends E> c) {
if (c instanceof BitList) { if (c instanceof BitList) {
rootSet.or(((BitList<? extends E>) c).rootSet); rootSet.or(((BitList<? extends E>) c).rootSet);
if (((BitList<? extends E>) c).hasMoreElementInTailList()) { if (((BitList<? extends E>) c).hasMoreElementInTailList()) {
@ -325,7 +325,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public int lastIndexOf(Object o) { public synchronized int lastIndexOf(Object o) {
int bitIndex = -1; int bitIndex = -1;
int index = -1; int index = -1;
if (CollectionUtils.isNotEmpty(tailList)) { if (CollectionUtils.isNotEmpty(tailList)) {
@ -344,22 +344,22 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public boolean isEmpty() { public synchronized boolean isEmpty() {
return this.rootSet.isEmpty() && CollectionUtils.isEmpty(tailList); return this.rootSet.isEmpty() && CollectionUtils.isEmpty(tailList);
} }
@Override @Override
public ListIterator<E> listIterator() { public synchronized ListIterator<E> listIterator() {
return new BitListIterator<>(this, 0); return new BitListIterator<>(this, 0);
} }
@Override @Override
public ListIterator<E> listIterator(int index) { public synchronized ListIterator<E> listIterator(int index) {
return new BitListIterator<>(this, index); return new BitListIterator<>(this, index);
} }
@Override @Override
public BitList<E> subList(int fromIndex, int toIndex) { public synchronized BitList<E> subList(int fromIndex, int toIndex) {
BitSet resultSet = (BitSet) rootSet.clone(); BitSet resultSet = (BitSet) rootSet.clone();
List<E> copiedTailList = tailList == null ? null : new LinkedList<>(tailList); List<E> copiedTailList = tailList == null ? null : new LinkedList<>(tailList);
if (toIndex < size()) { if (toIndex < size()) {
@ -414,7 +414,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public boolean hasNext() { public synchronized boolean hasNext() {
if (isInTailList) { if (isInTailList) {
return tailListIterator.hasNext(); return tailListIterator.hasNext();
} else { } else {
@ -428,7 +428,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public E next() { public synchronized E next() {
if (isInTailList) { if (isInTailList) {
if (tailListIterator.hasNext()) { if (tailListIterator.hasNext()) {
index += 1; index += 1;
@ -457,7 +457,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public boolean hasPrevious() { public synchronized boolean hasPrevious() {
if (isInTailList) { if (isInTailList) {
boolean hasPreviousInTailList = tailListIterator.hasPrevious(); boolean hasPreviousInTailList = tailListIterator.hasPrevious();
if (hasPreviousInTailList) { if (hasPreviousInTailList) {
@ -471,7 +471,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public E previous() { public synchronized E previous() {
if (isInTailList) { if (isInTailList) {
boolean hasPreviousInTailList = tailListIterator.hasPrevious(); boolean hasPreviousInTailList = tailListIterator.hasPrevious();
if (hasPreviousInTailList) { if (hasPreviousInTailList) {
@ -503,17 +503,17 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public int nextIndex() { public synchronized int nextIndex() {
return hasNext() ? index + 1 : index; return hasNext() ? index + 1 : index;
} }
@Override @Override
public int previousIndex() { public synchronized int previousIndex() {
return index; return index;
} }
@Override @Override
public void remove() { public synchronized void remove() {
if (lastReturnedIndex == -1) { if (lastReturnedIndex == -1) {
throw new IllegalStateException(); throw new IllegalStateException();
} else { } else {
@ -533,17 +533,17 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public void set(E e) { public synchronized void set(E e) {
throw new UnsupportedOperationException("Set method is not supported in BitListIterator!"); throw new UnsupportedOperationException("Set method is not supported in BitListIterator!");
} }
@Override @Override
public void add(E e) { public synchronized void add(E e) {
throw new UnsupportedOperationException("Add method is not supported in BitListIterator!"); throw new UnsupportedOperationException("Add method is not supported in BitListIterator!");
} }
} }
public ArrayList<E> cloneToArrayList() { public synchronized ArrayList<E> cloneToArrayList() {
if (rootSet.cardinality() == originList.size() && (CollectionUtils.isEmpty(tailList))) { if (rootSet.cardinality() == originList.size() && (CollectionUtils.isEmpty(tailList))) {
return new ArrayList<>(originList); return new ArrayList<>(originList);
} }
@ -553,7 +553,7 @@ public class BitList<E> extends AbstractList<E> implements Cloneable {
} }
@Override @Override
public BitList<E> clone() { public synchronized BitList<E> clone() {
return new BitList<>( return new BitList<>(
originList, (BitSet) rootSet.clone(), tailList == null ? null : new LinkedList<>(tailList)); originList, (BitSet) rootSet.clone(), tailList == null ? null : new LinkedList<>(tailList));
} }

View File

@ -22,6 +22,8 @@ import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -576,4 +578,44 @@ class BitListTest {
set.add(new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G"))); set.add(new LinkedList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G")));
Assertions.assertEquals(2, set.size()); Assertions.assertEquals(2, set.size());
} }
@Test
void testConcurrent() throws InterruptedException {
for (int i = 0; i < 100000; i++) {
BitList<String> bitList = new BitList<>(Collections.singletonList("test"));
bitList.remove("test");
CountDownLatch countDownLatch = new CountDownLatch(1);
CountDownLatch countDownLatch2 = new CountDownLatch(2);
Thread thread1 = new Thread(() -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
bitList.add("test");
countDownLatch2.countDown();
});
AtomicReference<BitList<String>> ref = new AtomicReference<>();
Thread thread2 = new Thread(() -> {
try {
countDownLatch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ref.set(bitList.clone());
countDownLatch2.countDown();
});
thread1.start();
thread2.start();
countDownLatch.countDown();
countDownLatch2.await();
Assertions.assertDoesNotThrow(() -> ref.get().iterator().hasNext());
}
}
} }