Java Programming: Three Correct Ways to Delete List Elements

Deleting elements from a List creates two problems:

  1. The number of elements in the List will change after the element is deleted;
  2. Deleting a List may cause concurrency problems;

We demonstrate correct deletion logic with code samples

package com.ips.list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class ArrayListRemove {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<String>();
        list.add("beijing");
        list.add("shanghai");
        list.add("shanghai");
        list.add("guangzhou");
        list.add("shenzhen");
        list.add("hangzhou");
        remove11(list, "shanghai");

    }

    private static void print(List<String> list){
        for (String item : list) {
            System.out.println("元素值:" + item);
        }
    }

    /*
     * 错误
     */
    public static void remove11(List<String> list, String target){
        int size = list.size();
        for(int i = 0; i < size; i++){
            String item = list.get(i);
            if(target.equals(item)){
                list.remove(item);
            }
        }
        print(list);
    }
    /*
     * 错误
     */
    public static void remove12(List<String> list, String target){
        for(int i = 0; i < list.size(); i++){
            String item = list.get(i);
            if(target.equals(item)){
                list.remove(item);
            }
        }
        print(list);
    }
    /*
     * 错误
     */
    public static void remove13(List<String> list, String target){
        int size = list.size();
        for(int i = size - 1; i >= 0; i--){
            String item = list.get(i);
            if(target.equals(item)){
                list.remove(item);
            }
        }
        print(list);
    }
    /*
     * 正确
     */
    public static void remove14(List<String> list, String target){
        for(int i = list.size() - 1; i >= 0; i--){
            String item = list.get(i);
            if(target.equals(item)){
                list.remove(item);
            }
        }
        print(list);
    }

    /*
     * 错误
     */
    public static void remove21(List<String> list, String target){
        for(String item : list){
            if(target.equals(item)){
                list.remove(item);
            }
        }
        print(list);
    }

    /*
     * 正确
     */
    public static void remove22(ArrayList<String> list, String target) {
        final CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<String>(list);
        for (String item : cowList) {
            if (item.equals(target)) {
                cowList.remove(item);
            }
        }
        print(cowList);
    }

    /*
     * 错误
     */
    public static void remove31(List<String> list, String target){
        Iterator<String> iter = list.iterator();
        while (iter.hasNext()) {
            String item = iter.next();
            if (item.equals(target)) {
                list.remove(item);
            }
        }
        print(list);
    }
    /*
     * 正确
     */
    public static void remove32(List<String> list, String target){
        Iterator<String> iter = list.iterator();
        while (iter.hasNext()) {
            String item = iter.next();
            if (item.equals(target)) {
                iter.remove();
            }
        }
        print(list);
    }

}

When the remove11 method is executed, the following error occurs:

Exception in thread “main” java.lang.IndexOutOfBoundsException: Index: 5, Size: 5 
at java.util.ArrayList.rangeCheck(ArrayList.java:635) 
at java.util.ArrayList.get(ArrayList.java:411) 
at com.ips.list.ArrayListRemove.remove11(ArrayListRemove.java:33) 
at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)

Since int size = list.size(); obtains the size of the List in advance, two elements are deleted in the for loop, resulting in an array out-of-bounds problem.

When the remove12 method is executed, the following error occurs:

元素值:beijing 
元素值:shanghai 
元素值:guangzhou 
元素值:shenzhen 
元素值:hangzhou

The string "shanghai" is not deleted. This method solves the problem of array out-of-bounds, but it does not solve the problem of completely deleting the data. The reason is this, trace the ArrayList.remove(Object 0) method:

 public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

When the element is deleted, the else logic is executed, and the fastRemove(index) method is called:

private void fastRemove(int index) {
        modCount++;
        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
    }

Through the code, we found that the logic of List deleting elements is to move the element after the target element forward by one index position, the last element is set to null, and size - 1; this also explains why the second "shanghai" is not selected. delete.

Execute the remove13 method, correct:

元素值:beijing 
元素值:guangzhou 
元素值:shenzhen 
元素值:hangzhou

Execute the remove14 method, correct:

元素值:beijing 
元素值:guangzhou 
元素值:shenzhen 
元素值:hangzhou

So what's the difference between remove13 and remove14? The answer is no difference, but there is a difference between remove11 and remove12. In remove12, the size value will be calculated every time for(int i = 0; i < list.size(); i++) is executed, which is more performance-intensive.

When the remove21 method is executed, the following error occurs:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
    at java.util.ArrayList$Itr.next(ArrayList.java:831)
    at com.ips.list.ArrayListRemove.remove21(ArrayListRemove.java:82)
    at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)

A java.util.ConcurrentModificationException exception is generated. The foreach method is actually a shorthand for the correct Iterable, hasNext, and next methods. So we start with List.iterator() and trace the iterator() method, which returns an Itr iterator object.

public Iterator<E> iterator() {
        return new Itr();
    }

Itr class definition code:

 private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        public boolean hasNext() {
            return cursor != size;
        }

        @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];
        }

        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();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

Through the code, we found that Itr is a private inner class defined in ArrayList. The checkForCommodification method is called in the next and remove methods. The function of this method is to judge whether modCount != expectedModCount is equal, and if not, throw ConcurrentModificationException. After each normal execution of the remove method, the value of expectedModCount = modCount will be assigned to ensure that the two values ​​are equal, then the problem is basically clear, execute list.remove(item); in the foreach loop, and perform the modCount value of the list object. The expectedModCount value of the iterator of the list object is not modified, so a ConcurrentModificationException is thrown.

Execute the remove22 method, correct:

元素值:beijing 
元素值:guangzhou 
元素值:shenzhen 
元素值:hangzhou 
通过 CopyOnWriteArrayList 解决了 List的并发问题。

When the remove31 method is executed, the following error occurs:

Exception in thread “main” java.util.ConcurrentModificationException 
at java.util.ArrayListItr.checkForComodification(ArrayList.java:859)atjava.util.ArrayListItr.next(ArrayList.java:831) 
at com.ips.list.ArrayListRemove.remove31(ArrayListRemove.java:109) 
at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)

Consistent with the exception generated by executing remove21, the cause of the problem is also the same.

Execute the remove32 method, correct:

元素值:beijing 
元素值:guangzhou 
元素值:shenzhen 
元素值:hangzhou

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324992155&siteId=291194637