introduction
A few days ago, a reader read Which traversal efficiency of ArrayList is the best, do you really understand it? " Why can't an ordinary for loop ArrayList delete two consecutive elements? In fact, this description is not correct. The correct way is to delete the normal for loop in positive order. You can't delete consecutive elements, so this article is generated.
How to delete data in ArrayList
Let's take a look at several ways to delete elements in ArrayList.
package com.workit.demo.array; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Predicate; /** * @author: * @Date: 2020/6/9 * @Description: */ public class ArrayListDelete { public static void main(String[] args) { Predicate<String> predicate = p -> p.equals("a") || p.equals("b"); // It can be deleted normally. The result is correct deleteByIterator(getList(), predicate); // It can be deleted normally. The result is correct deleteByReverseOrder(getList(), predicate); // Can delete the result is incorrect deleteByOrder(getList(), predicate); // Error cannot be deleted java.util.ConcurrentModificationException deleteByArrayList(getList(), predicate); // Error cannot be deleted java.util.ConcurrentModificationException deleteByForeach(getList(), predicate); //Error cannot be deleted java.util.ConcurrentModificationException deleteByEnhancedForLoop(getList(), predicate); // Delete data normally deleteAll(getList(), predicate); } public static List<String> getList() { List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); return list; } /** * Normal for loop reverse delete * It can be deleted normally. The result is correct * @param list * @param predicate */ public static void deleteByReverseOrder(List<String> list, Predicate<String> predicate) { for (int i = list.size() - 1; i >= 0; i--) { if (predicate.test(list.get(i))) { list.remove(list.get(i)); } } System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+":"+list.toString()); } /** * Normal for loop positive order deletion *Can delete the result is incorrect * @param list * @param predicate */ public static void deleteByOrder(List<String> list, Predicate<String> predicate) { for (int i = 0; i < list.size(); i++) { if (predicate.test(list.get(i))) { list.remove(list.get(i)); } } System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+":"+list.toString()); } /** * Iterator loop, delete using the remove() method of ArrayList * Can delete the result is incorrect * @param list * @param predicate */ public static void deleteByArrayList(List<String> list, Predicate<String> predicate) { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { if (predicate.test(iterator.next())) { list.remove(iterator.next()); } } System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+":"+list.toString()); } /** * Iterator loop, using the iterator's remove() method to delete * It can be deleted normally. The result is correct * @param list * @param predicate */ public static void deleteByIterator(List<String> list, Predicate<String> predicate) { Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { if (predicate.test(iterator.next())) { iterator.remove(); } } System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+":"+list.toString()); } /** * java8 forEach Method delete *Error cannot be deleted java.util.ConcurrentModificationException * @param list * @param predicate */ public static void deleteByForeach(List<String> list, Predicate<String> predicate) { list.forEach(p -> { if (predicate.test(p)) { list.remove(p); } }); System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+":"+list.toString()); } /** * Enhanced for loop delete *Error cannot be deleted java.util.ConcurrentModificationException * @param list * @param predicate */ public static void deleteByEnhancedForLoop(List<String> list, Predicate<String> predicate) { for (String string : list) { if (predicate.test(string)) { list.remove(string); } } } } /** * Call bulk delete method * @param list * @param predicate */ public static void deleteAll(List<String> list, Predicate<String> predicate) { List<String> removeList = new ArrayList<>(); for (String string : list) { if (predicate.test(string)) { removeList.add(string); } } list.removeAll(removeList); System.out.println(Thread.currentThread().getStackTrace()[1].getMethodName()+":"+list.toString()); }
Let's analyze why some of these methods can delete elements correctly and some can't. Quoting the words that the big guys often say that there is no secret under the source code, let's start with the source code.
Enhanced for loop removal & iterator recycling ArrayList.remove() method delete
- for loop deletion (deletebyenhanced forloop) and iterator loop, both of which will be thrown if you use the remove() method of ArrayList to delete (deleteByArrayList) java.util.ConcurrentModificationException In essence, they are iterator loops. Each loop will check the value of modCount and expectedModCount with the method of checkforcomoodification.
@SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
The List deletion method adds 1 to modCount every time it is deleted. The expectedModCount value remains unchanged.
private void fastRemove(int index) { modCount++; //modCount will add 1 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 }
So both of the above methods throw a ConcurrentModificationException.
java8 forEach method delete (throw exception)
-
Why does java8 forEach method delete * * ConcurrentModificationException? The answer is still in the source code.
As above, after deleting an element, modCount adds 1 and expectedModCount does not change.public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { // Is that the judgment again throw new ConcurrentModificationException(); }
The reason why continuous elements cannot be deleted by positive order deletion
-
for loop positive order deletion (deleteByOrder), which can be deleted but has incorrect result
Let's take a picture first. It's more intuitive.
The array needs to be moved every time it removes an element. After the first deletion (i=0), the subscript of B will be 0. Then, when the second deletion (i=1), will b be successfully omitted. (this can be avoided by reverse order circular deletion) what's the solution if we have to use positive order circular deletion? The way is that some of them only need to modify the value of i after deleting. The code is as follows:
for (int i = 0; i < list.size(); i++) { if (predicate.test(list.get(i))) { list.remove(list.get(i)); // Add the value of this correction i i--; } }
Do you get another operation.
remove() method using iterator (recommended)
Iterator loop, use the iterator's remove() method to delete (deleteByIterator). This is relatively simple. Let's directly look at the deletion of the iterator
The key code is one line expectedModCount = modCount
public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; //There is one more sentence after calling the delete method of ArrayList. } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } }
This method is also recommended by Ali Development Manual (the need to reply to official account: Taishan).
summary
A series of deletion methods are listed above. If you don't use them properly, you will step on them. I can't remember so much? The best way is not to delete data while looping. If you have to delete it? I suggest that you can use the bulk delete method (I've tried many times) or the iterator's remove() method.
end
- Due to their lack of talent and learning, there will inevitably be mistakes. If you find the wrong place, please leave a message for me to point out, and I will correct it.
- If you think the article is not bad, your forwarding, sharing, appreciation, likes and comments are my biggest encouragement.
- Thank you for reading, welcome and thank you for your attention.