@SpringBootTest
public class RemoveTest {
List<Transaction> list = new ArrayList<>();
@Before
public void init() {
list.add(new Transaction("test123"));
list.add(new Transaction("123test"));
}
@After
public void print() {
list
.stream()
.forEach(System.out::println);
}
/**
* 删除条件
* @param transaction
* @return
*/
public boolean needDel(Transaction transaction) {
return Character.isDigit(transaction.getReferenceCode().charAt(0));
}
/**
* foreach 的时候 修改集合(新增/删除) 会触发fast-fail机制,报错
*/
@Test
public void test0() {
for(Transaction t:list) {
if(this.needDel(t)) {
list.remove(t);
}
}
}
/**
* remove if
*/
@Test
public void test1() {
list.removeIf(this::needDel);
}
/**
* 使用迭代器删除
*/
@Test
public void test2() {
Iterator<Transaction> iterator = list.iterator();
while (iterator.hasNext()){
Transaction transaction = iterator.next();
if(this.needDel(transaction)) {
iterator.remove();
}
}
}
/**
* stream filter
*/
@Test
public void test3() {
list = list
.stream()
.filter(v->!this.needDel(v))
.collect(Collectors.toList());
}
/**
* 使用并发安全的集合类删除
*/
@Test
public void test4() {
list = new CopyOnWriteArrayList<>(list);
for(Transaction t:list) {
if(this.needDel(t)) {
list.remove(t);
}
}
}
}
fast-fail机制
在系统设计的时候优先考虑异常的情况, 一旦发生异常, 直接停止并上报
例:
集合中的fast-fail机制:
在test0方法中, 在集合遍历的过程中添加删除元素, 会抛出ConcurrentModificationException,
在遍历的过程中, 检测到对象的并发修改, 但不允许这种修改, 就会抛出该异常
在集合的内部存在两个变量
modCount:表示该集合实际被修改的次数
expectModCount:表示迭代器预期,该集合被修改的次数, 只有通过迭代器对该集合操作时,该值才会改变
在遍历的过程中检测到两个值不相等,就会抛出ConcurrentModificationException,提示并发修改
fail-safe机制:
为了避免fast-fail机制, java 提供了fail-safe机制的集合类(java.util.concurrent包下),例如CopyOnWriteArrayList, 可以在多线程下并发修改
CopyOnWriteArrayList在添加/删除元素时, 不直接添加元素, 先将原容器复制生成一个新的容器, 在新的容器上添加/删除元素, 之后再用原容器指向新容器, 才用了读写分离的思想, 在迭代的过程中不需要检测fast-fail异常
但是在迭代过程中也不能检测原容器的修改,例如:
userNames的输出值不变
CopyOnWrite 对比 Vector:
CopyOnWrite:读写分离, 读和写不同的容器
Vector:读写相同的容器, 读写互斥(加锁)