List-remove
Iterator-Enumeration
// Enumeration只实现了遍历
public interface Enumeration<E> {
boolean hasMoreElements();
E nextElement();
}
// 除了遍历,还支持删除,有自己的删除函数remove
public interface Iterator<E> {
boolean hasNext();
E next();
// 支持删除元素
default void remove() {
// fail-fast 方法
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
List-remove
public class DelFromList {
/**
* 1. 需要删除重复的元素,删除元素时数组长度变化,对应下标位置会发生变化
* 1. List<String> list =new ArrayList<>(); 可以删除
* 2. List<String> list = Arrays.asList("1", "2", "3", "4", "4", "5"); => 返回特定长度的数据,不能删除
*/
private static void del1() {
// 返回特定长度的数据,不能删除
// List<String> list = Arrays.asList("1", "2", "3", "4", "4", "5");
// 可以删除元素,但需要注意重复元素
List<String> list = new ArrayList<>();
list.addAll(Arrays.asList("1", "2", "3", "4", "4", "5"));
for (int i = 0; i < list.size(); i++) {
if (list.get(i).equals("4")) {
list.remove(i);
// 重点->需要注意重复的元素,数组长度变化
i--;
}
}
printList(list);
}
/**
* 1. 使用forearch遍历的时候,此时队列不能使用remove/add,会出现=> java.util.ConcurrentModificationException
*/
private static void del2() {
List<String> list = new ArrayList<>();
list.addAll(Arrays.asList("1", "2", "3", "4", "4", "5"));
// foreach不能删除List
// 1. forearch遍历是在JVM层通过Iterator实现的遍历,Iterator有提供自己的remove函数(看源码)
// 2. 但是代码中元素的的删除是使用了List接口实现的删除(remove/add),这两个接口实现的逻辑不一样
for (String s : list) { // iterator
if (s.equals("4")) {
list.remove(s); //list
}
}
printList(list);
}
// 推荐的删除方法
private static void del3() {
List<String> list = Arrays.asList("1", "2", "3", "4", "4", "5");
Iterator<String> it = list.iterator();
while (it.hasNext()) { // iterator
String val = it.next();
if (val.equals("4")) {
it.remove(); // iterator
}
}
}
private static <T> void printList(List<T> list) {
for (T t : list) {
System.out.print(t + ", ");
}
}
private static <T> void printList2(Vector<T> vector) {
Enumeration<T> enumeration = vector.elements();
while (enumeration.hasMoreElements()) {
System.out.print(enumeration.nextElement() + ", ");
}
}
public static void main(String[] args) {
del2();
}
}
fail-fast
- java.util.ConcurrentModificationException 这个异常就是触发了fail-fast;
- 定义: jvm检测到多个线程对数据集合做修改操作时触发异常(但是遍历是在单线程环境,因为调用的remove接口不一致)
// Iterator 抛出异常的地方
final void checkForComodification() {
if (modCount != expectedModCount) throw new ConcurrentModificationException();
}
// 1. modCount是ArrayList中的一个成员变量,它表示该集合实际被修改的次数。
// 2. expectedModCount 是 ArrayList中的一个内部类——Itr中的成员变量。expectedModCount表示这个迭代器期望该集合被修改的次数。其值是在ArrayList.iterator方法被调用的时候初始化的。只有通过迭代器对集合进行操作,该值才会改变。
// 3. Itr是一个Iterator的实现,使用ArrayList.iterator方法可以获取到的迭代器就是Itr类的实例
// AyyayList的remove,只修改modCount
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
// Iterator的remove, 2个变量同时修改
public void remove() {
if (lastRet < 0) throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
fail-safe
- java.util.concurrent包下的容器都是安全失败的,支持并发修改
- HashMap的替代类是
ConcurrentHashMap,实现原理是通过分段锁; - ArrayList的替代类是
CopyOnWriterArrayList,实现方式是备份数据,只保证最终的一致性 volitaileHolder
对比
都是线程安全的解决方案
- fail-fast,针对线程不安全的容器,发现并发修改操作时快速抛出异常
- fail-safe,针对线程安全容器,不允许null,支持并发操作,实现的方式锁|cow
线程安全解决方案
- 锁
- 数据分片
- cow(数据备份)