基于java构造方法Vector删除元素源码分析

目录
  • 前言
  • remove(int)方法分析
  • remove(Object)方法分析
  • removeElement(Object)方法分析
  • removeElementAt(int)方法分析
  • removeIf()方法分析
  • removeAllElement()方法分析
  • removeAll(Collection)方法分析
  • 父类中的removeAll(Collection)方法分析
  • retainAll(Collection)方法分析
  • 总结

(注意:本文基于JDK1.8)

前言

包括迭代器中的remove()方法,以及删除单个元素、删除多个元素、删除所有元素、删除不包含的所有元素的方法,Vector中共计10个对外的API可以用于删除元素,今天一起分析每一个删除元素的方法是如何实现的!

remove(int)方法分析

    public synchronized E remove(int index) {
        modCount++;
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        E oldValue = elementData(index);
        int numMoved = elementCount - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--elementCount] = null; // Let gc do its work

        return oldValue;
    }

用于删除指定下标单个元素的方法,传入的参数index表示元素的下标,第一个元素的下标是0,这个基础知识点不要忘记哦

1、为fail-fast机制保驾护航

modCount是Vector对象持有的一个int变量,它本身位于Vector的父类AbstractList中,此处增加1,表示Vector的元素状态发生改变,迭代器那里会使用fail-fast,防止多线程下即遍历又删除,也防止单线程下,一边遍历元素、一边删除元素

2、检查下标是否存在元素

检查传入的下标index是否存在元素,当index与elementCount相等或者大于elementCount,此处的index并没有元素,所以不能删除没有元素的位置,此处作者抛出ArrayIndexOutOfBoundsException对象,为此告知调用者,你传入的下标根本没有元素,怎么删除呢?

3、保存删除的元素到局部变量

调用elementData元素,并传入下标index,获得指定下标处的元素,并由局部变量oldValue负责保存

4、计算需要挪动元素的数量

使用表示元素总数的elementCount减去index、减去1,得到需要挪动元素的数量并存储到局部变量numMoved

5、挪动元素

如果需要挪动元素,就将index下标后面的所有元素向前挪动(复制)

6、减少元素总数值

先将元素总数elementCount减去1

7、将持有元素的引用,赋值为null

将Vector对象持有的数组elementData对象的指定下标处,赋值为null,GC会删除没有Root结点对象连接的对象

8、向调用者返回删除后的元素

return会返回此时被删除的元素对象

remove(Object)方法分析

    public boolean remove(Object o) {
        return removeElement(o);
    }

用于将第一个匹配的元素对象删除的方法,传入的参数为元素对象,此方法并没有使用synchronized修饰,那么它如何保证线程安全的删除元素呢?往下看……

1、实际调用removeElement()方法

2、向调用者返回删除结果

removeElement(Object)方法分析

    public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
    }

用于删除元素的方法,使用synchronized修饰,同一时刻只有获得对象锁的线程可以执行该方法,未获得对象锁的线程,将被阻塞在方法的入口处,传入的1个参数表示元素对象

1、fail-fast机制保护

modCount增加1,表示Vector持有的元素发生改变

2、获取元素对象在数组中的下标

调用index()方法,同时会将元素对象ob传入进去,返回值则由局部变量i负责存储,它存储的是元素在数组中下标

3、元素存在,则继续执行删除工作

当局部变量i的值大于等于0,说明元素存储在数组中(Vector对象持有一个数组对象,用于保存元素的引用),通过调用removeElement()方法完成删除工作,最后向调用者返回true,表示删除元素成功

4、当元素不存在时,向调用者返回false

removeElementAt(int)方法分析

    public synchronized void removeElementAt(int index) {
        modCount++;
        if (index >= elementCount) {
            throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                     elementCount);
        }
        else if (index < 0) {
            throw new ArrayIndexOutOfBoundsException(index);
        }
        int j = elementCount - index - 1;
        if (j > 0) {
            System.arraycopy(elementData, index + 1, elementData, index, j);
        }
        elementCount--;
        elementData[elementCount] = null; /* to let gc do its work */
    }

用于删除指定下标的元素,使用synchronized修饰,同一时刻只有1个线程可以执行该方法,其它未获得对象锁的线程将被阻塞在入口处,传入的1个参数index表示元素的下标

1、fail-fast机制

modCount增加1,表示Vector保存的元素发生改变

2、检查下标是否合理

当传入的下标index大于等于Vector对象持有的elementCount值时,抛出ArrayIndexOutOfBoundsException,告知调用者,index >= xx值

当传入的下标index小于0时,同样抛出ArrayIndexOutOfBoundsException对象,此时只告知index值是多少

只有下标0至elementCount - 1的范围内,才有元素,所以作者的保护相当的合理

3、计算需要移动元素的数量

比如一共保存了5个元素(elementCount)、需要删除下标为3的元素,下标为3的元素是第4个元素,后续需要挪动的元素数量为1,所以

公式为:remove_num = elementCount - index - 1,我们再套进来公式里:remove_num = 5 - 3 - 1

4、开始挪动元素

挪动元素,而采用的是复制元素,system类的静态方法arrycopy即可做到,它接受5个参数

第一个参数:表示需要从哪个数组对象中复制元素(源头)

第二个参数:表示需要从数组对象的哪个下标处,开始复制

第三个参数:表示需要粘贴到哪个数组对象中(目标)

第四个参数:表示需要粘贴到数组对象的起始下标

第五个参数:表示共计复制几个元素

5、记录的元素总数减去1

elementCount减少1

6、将剩下的数组中,多余的引用,删除掉

因为每个元素都向前复制了一位,所以此时的elementCount指向的下标处,还存着对象的引用,这会造成对象无法被GC回收,赋值为null,由GC回收对象占用的内存空间

removeIf()方法分析

    public synchronized boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        // figure out which elements are to be removed
        // any exception thrown from the filter predicate at this stage
        // will leave the collection unmodified
        int removeCount = 0;
        final int size = elementCount;
        final BitSet removeSet = new BitSet(size);
        final int expectedModCount = modCount;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            @SuppressWarnings("unchecked")
            final E element = (E) elementData[i];
            if (filter.test(element)) {
                removeSet.set(i);
                removeCount++;
            }
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }

        // shift surviving elements left over the spaces left by removed elements
        final boolean anyToRemove = removeCount > 0;
        if (anyToRemove) {
            final int newSize = size - removeCount;
            for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
                i = removeSet.nextClearBit(i);
                elementData[j] = elementData[i];
            }
            for (int k=newSize; k < size; k++) {
                elementData[k] = null;  // Let gc do its work
            }
            elementCount = newSize;
            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            modCount++;
        }
        return anyToRemove;
    }

实现Collection接口的方法,用于根据指定条件删除元素的方法,同样由synchronized修饰,同一时刻只有获取到当前对象锁的线程可以调用此方法,其它线程如果也调用此方法,会被阻塞在方法的入口处

1、检查传入的Predicate对象

确保Predicate对象必须传入,此处使用Objects的静态方法requireNonNull()检查

2、创建用于记录删除数量的局部变量

removeCount,默认值为0

3、临时存储当前Vector对象持有的元素总数

创建一个局部变量size用于存储当前元素总数elementCount

4、创建BitSet对象

利用Vector对象持有的元素总数size,用于创建一个BitSet对象,局部变量removeSet临时指向该此BitSet对象

removeAllElement()方法分析

    public synchronized void removeAllElements() {
        modCount++;
        // Let gc do its work
        for (int i = 0; i < elementCount; i++)
            elementData[i] = null;
         elementCount = 0;
    }

用于删除Vector对象持有的所有元素对象

1、fail-fast机制保护

实例变量modCount增加1,表示Vector持有的元素发生变化

2、遍历数组对象

将Vector对象持有的数组对象elementData中实际保存元素对象引用的所有位置,全部赋值为null,当对象从GC Roots处不可达时,垃圾收集器会回收对象占用的内存空间

3、元素总数标记为0

Vector对象持有的elementCount标记为0,说明Vector对象不再持有任何元素

removeAll(Collection)方法分析

    public synchronized boolean removeAll(Collection<?> c) {
        return super.removeAll(c);
    }

用于删除多个元素的方法,只有与传入的Collection对象中持有的元素匹配的元素会被删除

1、直接调用父类的removeAll()方法,并将传入的Collection对象传入进去

2、向调用者返回删除元素的结果

父类中的removeAll(Collection)方法分析

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }

位于父类AbstractCollection中,用于删除与传入参数Collection对象中匹配的所有元素

1、检查传入参数Collection对象

2、定义局部变量,表示是否修改,默认值false

3、调用iterator()方法获取迭代器对象,并由局部变量it负责保存

此iterator()方法都是子类去实现,Vector中也实现了该方法,此方法会返回一个迭代器对象

4、使用迭代器对象的方法进行遍历与删除

hasNext()方法用于判断是否有下一个元素,当第一次使用时,判断的是第一个元素

next()方法可以获取到一个元素,第一次使用时,获取到的是第一个元素

remove()方法可以删除一个元素

每当删除一个元素(Vector中持有的元素与Collection中的某个元素相同),将是否修改的标志位modified赋值为true

5、向调用者返回删除结果

retainAll(Collection)方法分析

    public synchronized boolean retainAll(Collection<?> c) {
        return super.retainAll(c);
    }

用于删除除了传入的Collection对象持有的元素之外的所有元素,求交集……

总结

1、即可以删除一个元素、也可以删除多个元素

2、fail-fast机制除了保护多线程下的使用,也防止在单线程下即遍历、又删除

3、为了规避一边遍历,一边删除的锅,可以使用迭代器对象提供的一边遍历、一边删除的方法

以上就是基于java构造方法Vector删除元素源码分析的详细内容,更多关于java构造方法Vector的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java编程中的vector类用法学习笔记

    java.util.vector提供了向量类(vector)以实现类似动态数组的功能.在Java语言中没有指针的概念,但如果正确灵活地使用指针又确实可以大大提高程序的质量.比如在c,c++中所谓的"动态数组"一般都由指针来实现.为了弥补这个缺点,Java提供了丰富的类库来方便编程者使用,vector类便是其中之一.事实上,灵活使用数组也可以完成向量类的功能,但向量类中提供大量的方法大大方便了用户的使用. 创建了一个向量类的对象后,可以往其中随意插入不同类的对象,即不需顾及类型也不需预先

  • 详解Java编程中向量(Vector)的应用

    Vector(向量)是 java.util 包中的一个类,该类实现了类似动态数组的功能. 向量和数组相似,都可以保存一组数据(数据列表).但是数组的大小是固定的,一旦指定,就不能改变,而向量却提供了一种类似于"动态数组"的功能,向量与数组的重要区别之一就是向量的容量是可变的. 可以在向量的任意位置插入不同类型的对象,无需考虑对象的类型,也无需考虑向量的容量. 向量和数组分别适用于不同的场合,一般来说,下列场合更适合于使用向量: 如果需要频繁进行对象的插入和删除工作,或者因为需要处理的对

  • Java基础之容器Vector详解

    一.前言 知识补充:Arrays.copyOf函数: public static int[] copyOf(int[] original, int newLength) { int[] copy = new int[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; } 可见copyOf()在内部新建一个数组,调用arrayCopy()将ori

  • 基于java构造方法Vevtor添加元素源码分析

    目录 前言 add(E)方法分析 add(int,E)方法分析 insertElementAt()方法分析 addElement()方法分析 addAll()方法分析 addAll(int,Collection)方法分析 ListItr中的add()方法分析 总结 (注意:本文基于JDK1.8) 前言 算上迭代器的add()方法,Vector中一共有7个添加元素的方法,5个添加单个元素的方法,2个添加多个元素的方法,接下来就一起分析它们的实现--Vector是一个线程安全的容器类,它的添加功能是

  • 详解Java中的Vector

    Vector实现了AbstractList抽象类和List接口,和ArrayList一样是基于Array存储的 Vector 是线程安全的,在大多数方法上存在synchronized关键字 //Vector存放的元素,初始化默认长度为10 protected Object[] elementData; //元素个数 protected int elementCount; //每次扩容大小,默认为0 protected int capacityIncrement; //构造函数,无指定初始化大小和

  • 基于java构造方法Vector创建对象源码分析

    目录 前言 构造方法Vector()分析 构造方法Vector(int)分析 构造方法Vecotor(int,int)分析 构造方法Vector(Collection)分析 重要字段介绍(不含基类中定义的字段) (注意:本文基于JDK1.8) 前言 Vector是线程安全的动态数组类,提供4个创建Vector对象的构造方法,接下来我们逐个分析每个创建Vector对象的构造方法 构造方法Vector()分析 public Vector() { this(10); } 用于创建Vector对象的默认

  • 基于java构造方法Vector删除元素源码分析

    目录 前言 remove(int)方法分析 remove(Object)方法分析 removeElement(Object)方法分析 removeElementAt(int)方法分析 removeIf()方法分析 removeAllElement()方法分析 removeAll(Collection)方法分析 父类中的removeAll(Collection)方法分析 retainAll(Collection)方法分析 总结 (注意:本文基于JDK1.8) 前言 包括迭代器中的remove()方

  • 基于java构造方法Vector修改元素源码分析

    目录 前言 set(int,E)方法分析 setElementAt(E,int)方法分析 总结 (注意:本文基于JDK1.8) 前言 增删改查,修改元素,Vector提供了3个方法,包括迭代器中的一个,不过本文只分析Vector自身的两个修改元素的方法,迭代器中的方法将单独分析 set(int,E)方法分析 public synchronized E set(int index, E element) { if (index >= elementCount) throw new ArrayInd

  • 基于java构造方法Vector查找元素源码分析

    目录 前言 get(int)方法分析 contains(Object)方法分析 containsAll()方法分析 indexOf(Object)方法分析 indexOf(Object,index)方法分析 lastIndexOf(Object)方法分析 elementAt(int)方法分析 firstElement()方法分析 lastElement()方法分析 elementData(int)方法分析 总结 (注意:本文基于JDK1.8) 前言 元素在存储到内存中,当我们需要使用在内存中存储

  • 基于java构造方法Vector遍历元素源码分析

    (注意:本文基于JDK1.8) 前言 任何一个容器类对象用于持有元素后,总是需要遍历元素的,即挨个去访问每个元素1次,而遍历元素,除了常规的依赖于数组对象的下标之外,更常用的是封装好的迭代器,今天就来学习Vector中的迭代器是如何设计的,与迭代器相关的方法有: iterator() listIterator() listIterator(int index) 3个Vector中的定义的方法,均会返回一个迭代器对象--简单说说这3个方法的来历 iterator()方法的来历 iterator()

  • Java HashSet添加 遍历元素源码分析

    目录 HashSet 类图 HashSet 简单说明 HashSet 底层机制说明 模拟数组+链表的结构 HashSet 添加元素底层机制 HashSet 添加元素的底层实现 HashSet 扩容机制 HashSet 添加元素源码 HashSet 遍历元素底层机制 HashSet 遍历元素底层机制 HashSet 遍历元素源码 HashSet 类图 HashSet 简单说明 1.HashSet 实现了 Set 接口 2.HashSet 底层实际上是由 HashMap 实现的 public Has

  • Java集合系列之LinkedHashMap源码分析

    这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHashMap源码之前,读者有必要先去了解HashMap的源码,可以查看我上一篇文章的介绍<Java集合系列[3]----HashMap源码分析>.只要深入理解了HashMap的实现原理,回过头来再去看LinkedHashMap,HashSet和LinkedHashSet的源码那都是非常简单的.因此,读

  • Java并发系列之ConcurrentHashMap源码分析

    我们知道哈希表是一种非常高效的数据结构,设计优良的哈希函数可以使其上的增删改查操作达到O(1)级别.Java为我们提供了一个现成的哈希结构,那就是HashMap类,在前面的文章中我曾经介绍过HashMap类,知道它的所有方法都未进行同步,因此在多线程环境中是不安全的.为此,Java为我们提供了另外一个HashTable类,它对于多线程同步的处理非常简单粗暴,那就是在HashMap的基础上对其所有方法都使用synchronized关键字进行加锁.这种方法虽然简单,但导致了一个问题,那就是在同一时间

  • Java并发系列之ReentrantLock源码分析

    在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可见性.在大多数情况下,这些机制都能很好地完成工作,但却无法实现一些更高级的功能,例如,无法中断一个正在等待获取锁的线程,无法实现限定时间的获取锁机制,无法实现非阻塞结构的加锁规则等.而这些更灵活的加锁机制通常都能够提供更好的活跃性或性能.因此,在Java5.0中增加了一种新的机制:Reentrant

  • Java并发系列之AbstractQueuedSynchronizer源码分析(概要分析)

    学习Java并发编程不得不去了解一下java.util.concurrent这个包,这个包下面有许多我们经常用到的并发工具类,例如:ReentrantLock, CountDownLatch, CyclicBarrier, Semaphore等.而这些类的底层实现都依赖于AbstractQueuedSynchronizer这个类,由此可见这个类的重要性.所以在Java并发系列文章中我首先对AbstractQueuedSynchronizer这个类进行分析,由于这个类比较重要,而且代码比较长,为了

  • Java并发系列之CountDownLatch源码分析

    CountDownLatch(闭锁)是一个很有用的工具类,利用它我们可以拦截一个或多个线程使其在某个条件成熟后再执行.它的内部提供了一个计数器,在构造闭锁时必须指定计数器的初始值,且计数器的初始值必须大于0.另外它还提供了一个countDown方法来操作计数器的值,每调用一次countDown方法计数器都会减1,直到计数器的值减为0时就代表条件已成熟,所有因调用await方法而阻塞的线程都会被唤醒.这就是CountDownLatch的内部机制,看起来很简单,无非就是阻塞一部分线程让其在达到某个条

随机推荐