(注:本文基于JDK1.8)
前言
包括迭代器中的()方法,以及删除单个元素、删除多个元素、删除所有元素、删除所有不包括元素的方法,总共有10个外部API可以用来删除元素。 今天我们就逐一分析一下。 删除元素的方法如何实现!
(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. 保护快速失败机制
它是一个由对象保存的 int 变量。 它位于父类中。 这里加1表示元素的状态发生了变化。 迭代器中会使用Fail-fast,防止多线程遍历和删除,也防止单线程下同时遍历和删除元素
2、检查下标是否有元素
检查传入的下标索引中是否存在元素。 当index等于或大于时 下标根本没有元素。 怎么删除呢?
3.将删除的元素保存到局部变量中
调用元素并传入下标索引即可获取指定下标处的元素并保存到局部变量中。
4.计算需要移动的元素数量
使用减法索引并减去1来表示元素总数,以获得需要移动的元素数量并将其存储在局部变量中。
5. 移动元素
如果需要移动某个元素,则将索引下标之后的所有元素向前移动(复制)
6.减少元素总数
首先将元素总数减1
7. 将持有元素的引用分配为 null。
在对象持有的数组对象的指定下标处将值赋为null,GC就会删除未连接到Root节点对象的对象。
8. 将删除的元素返回给调用者
会返回此时被删除的元素对象
()方法分析
public boolean remove(Object o) { return removeElement(o); }
用于删除第一个匹配元素对象的方法。 传入的参数是元素对象。 该方法不使用修改,那么它如何保证元素的线程安全删除呢? 向下看…
1. 实际的call()方法
2.向调用者返回删除结果
()方法分析
public synchronized boolean removeElement(Object obj) { modCount++; int i = indexOf(obj); if (i >= 0) { removeElementAt(i); return true; } return false; }
用于删除元素的方法,使用修改。 只有获得对象锁的线程才能同时执行该方法。 没有获得对象锁的线程会被阻塞在方法入口处。 传入的1个参数代表元素对象。
1. Fail-fast机制保护
加1表示持有的元素发生了变化
2.获取数组中元素对象的下标
调用index()方法,同时传入元素对象ob。 返回值由局部变量i存储,i存储的是数组中元素的下标。
3. 如果该元素存在,则继续删除工作。
当局部变量i的值大于等于0时,表示该元素存储在数组中(该对象保存了一个数组对象,用于保存该元素的引用)。 删除工作是通过调用()方法完成的,最后向调用者返回true,表示删除。 基本成功
4.当元素不存在时,返回false给调用者
(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 */ }
用于删除指定下标的元素。 使用修改后,同一时间只有一个线程可以执行该方法。 其他没有获得对象锁的线程会被阻塞在入口处。 传递的参数index代表元素的下标。
1. 快速失败机制
加1表示保存的元素发生了变化
2、检查出价是否合理
当传入的下标索引大于或等于对象持有的值时,抛出该异常,通知调用者index >= xx value
当传入的下标索引小于0时,该对象也会被抛出。 此时,仅告知索引值。
元素只存在于下标0到-1范围内,所以作者的保护还是比较合理的。
3.计算需要移动的元素数量
例如,总共保存了5个元素(),需要删除下标为3的元素。 下标为3的元素是第四个元素,后续需要移动的元素个数为1,所以
公式为:= - 索引 - 1,我们将其代入公式:= 5 - 3 - 1
4.开始移动元素
要移动元素,您可以使用类的静态方法来完成此操作,该方法接受 5 个参数。
第一个参数:表示需要从中复制元素的数组对象()
第二个参数:表示需要从数组对象的哪个下标复制。
第三个参数:表示需要粘贴到哪个数组对象()
第四个参数:表示需要粘贴到数组对象中的起始下标。
第五个参数:表示总共复制多少个元素
5、记录元素总数减1
减少 1
6. 删除剩余数组中的冗余引用。
因为每个元素都向前复制一位,所以此时指向的下标处仍然存在对该对象的引用。 这会导致该对象无法被GC回收,并且该值为null,并且该对象占用的内存空间将被GC回收。
()方法分析
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; }
实现该接口的方法用于根据指定条件删除元素。 也进行了修改。 同时,只有获得当前对象锁的线程才能调用该方法。 如果其他线程也调用该方法,则会被阻塞在该方法的入口处。
1.检查传入的对象
确保必须传入对象,这里使用静态()检查
2.创建一个局部变量来记录删除次数
,默认值为0
3.临时存储当前对象持有的元素总数
创建一个局部变量size来存储当前元素总数
4. 创建对象
使用对象持有的元素总数、大小来创建对象,局部变量临时指向该对象。
()方法分析
public synchronized void removeAllElements() { modCount++; // Let gc do its work for (int i = 0; i < elementCount; i++) elementData[i] = null; elementCount = 0; }
用于删除该对象持有的所有元素对象
1. Fail-fast机制保护
实例变量加1,表示持有的元素发生了变化。
2. 遍历数组对象
将实际保存元素对象引用的对象所保存的数组对象中的所有位置分配为 null。 当从 GC Roots 无法到达该对象时,垃圾收集器将回收该对象占用的内存空间。
3、元素总数标记为0
对象持有的标记为0,表示该对象不再持有任何元素。
()方法分析
public synchronized boolean removeAll(Collection<?> c) { return super.removeAll(c); }
删除多个元素的方法。 只有与传递的对象中保存的元素匹配的元素才会被删除。
1、直接调用父类的()方法,将传入的对象传入其中
2.向调用者返回删除元素的结果
父类中()方法分析
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; }
位于父类中,用于删除所有与传入的参数对象匹配的元素
1.检查传入的参数对象
2、定义局部变量,表示是否修改。 默认值为 false。
3、调用()方法获取迭代器对象,并将其保存到局部变量it中
这个()方法是由子类实现的,也是在中实现的。这个方法会返回一个迭代器对象。
4.使用迭代器对象方法进行遍历和删除
()方法用于判断是否有下一个元素。 第一次使用时,确定第一个元素。
next()方法可以获得一个元素。 第一次使用时,获取第一个元素。
()方法可以删除一个元素
每当删除一个元素( 中保存的元素与 中的元素相同)时,是否修改的标志位被赋值为true。
5.向调用者返回删除结果
()方法分析
public synchronized boolean retainAll(Collection<?> c) { return super.retainAll(c); }
用于删除除传入对象持有的元素之外的所有元素,求交集...
总结
1. 可以删除一个元素,也可以删除多个元素。
2、fail-fast机制不仅可以保护多线程下的使用,还可以防止单线程下的遍历和删除。
3、为了避免同时遍历和删除的问题,可以使用迭代器对象提供的同时遍历和删除的方法。
以上就是基于Java构造方法删除元素的源码分析的详细内容。 更多关于Java构造方法的信息,请关注 House其他相关文章!