java-jdk-List

103 阅读2分钟

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

对比

都是线程安全的解决方案

  1. fail-fast,针对线程不安全的容器,发现并发修改操作时快速抛出异常
  2. fail-safe,针对线程安全容器,不允许null,支持并发操作,实现的方式锁|cow

线程安全解决方案

  1. 数据分片
  2. cow(数据备份)