1. 程式人生 > >Java中如何優雅地刪除List中的元素

Java中如何優雅地刪除List中的元素

在工作中的許多場景下,我們都會使用到List這個資料結構,那麼同樣的有很多場景下需要刪除List中的某一個元素或某幾個元素,那麼我們該如何正確無誤地刪除List中的元素的,今天我來教大家三種方式。

前提知識準備

for迴圈的執行順序

這裡借用百度百科的一張圖,簡明扼要的介紹一下。

Iterator迭代器介紹

迭代器:迭代其實我們可以簡單地理解為遍歷,是一個標準化遍歷各類容器裡面的所有物件的方法類,它是一個很典型的設計模式。Iterator 模式是用於遍歷集合類的標準訪問方法。它可以把訪問邏輯從不同型別的集合類中抽象出來,從而避免向每次遍歷前都需要知道要遍歷集合的內部結構。 

// 遍歷list
List list = new ArrayList();
list.add(1);
list.add(2);
for (int i = 0; i < list.size(); i++) {
    Object object = list.get(i);
    // do something
}

// 遍歷map
Map<String,String> map = new HashMap<>();
map.put("1","first");
map.put("2","second");
for (Map.Entry<String,String> entry : map.entrySet()){
    String key = entry.getKey();
    String value = entry.getValue();
    // do something
}

對於這兩種方式,我們總是都事先知道集合的內部結構,訪問程式碼和集合本身是緊密耦合的,無法將訪問邏輯從集合類和遍歷方法中分離出來。同時每一種集合對應一種遍歷方法,程式碼無法複用。
為了解決以上問題, Iterator 模式騰空出世,它總是用同一種邏輯來遍歷集合。使得需要遍歷集合的人,在遍歷的時候不需要了解集合的內部結構,所有的內部狀態都由 Iterator 來維護。遍歷集合的方法不直接和集合類打交道,它總是控制 Iterator,向它傳送”向前”,”向後”,”取當前元素”的命令,就可以間接遍歷整個集合。

錯誤:for迴圈順序遍歷

直接使用簡單for迴圈,以for (int i = 0; i < list.size(); i++)

 進行遍歷,這種方式可能會在遍歷的過程中漏掉部分元素,從而出現少刪的情況。

/**
 * 通過簡單的遍歷方式,在遍歷的過程中有可能會漏掉元素
 * 取第二個元素i=1時,滿足條件被刪掉,原有的陣列的第三個元素,變成了新陣列的第二個元素
 * i++後i=2,但i=2指向的是新陣列中的第三個元素,那麼原陣列中的第三個元素就被漏掉了
 *
 * @param list
 * @param element
 * @return
 */
public static List forRemove(List list, Object element) {
    for (int i = 0; i < list.size(); i++) {
        if (element.equals(list.get(i))) {
            list.remove(i);
        }
    }
    return list;
}

錯誤:增強for迴圈,刪除後不退出

使用增強for迴圈是,如果刪除後繼續向下迴圈則會報java.util.ConcurrentModificationException

/**
 * 使用增強for迴圈是,如果刪除後繼續向下迴圈則會報
 * java.util.ConcurrentModificationException
 *
 * @param list
 * @param element
 * @return
 */
public static List forceForRemove(List list, Object element) {
    for (Object item : list) {
        if (item.equals(element)) {
            list.remove(item);
        }
    }
    return list;
}

異常如下:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at com.lingyejun.leetcode.RemoveListElement.forceForRemove(RemoveListElement.java:57)
    at com.lingyejun.leetcode.RemoveListElement.main(RemoveListElement.java:112)

正確:逆向迴圈遍歷

我們使用逆向遍歷的方式可以得到正確的結果

/**
 * 逆向迴圈,是正確的
 * 1-->2-->3-->4
 * 逆向迴圈時,倒數第一個元素滿足條件被刪除時,i--後,原陣列的倒數第二個變成了新陣列的倒數第一個元素
 * i = size-2指向新陣列的最後一個元素,沒有漏掉。
 * 同理倒數第二個元素滿足條件被刪除時,i--後,原陣列的倒數第三個變成了新陣列的倒數第二個元素
 * i= size-3指向新陣列的倒數第二個元素,也沒有漏掉
 *
 * @param list
 * @param element
 * @return
 */
public static List reverseorRemove(List list, Object element) {
    for (int i = list.size() - 1; i >= 0; i--) {
        if (element.equals(list.get(i))) {
            list.remove(i);
        }
    }
    return list;
}

正確但不優雅:增強for迴圈刪除元素後break

使用增強for迴圈,刪除元素後,立即跳出,則正常退出,但缺點是不能向後繼續迴圈了。  

/**
 * 刪除元素後,立即跳出,則正常退出,但不能向後繼續迴圈了
 *
 * @param list
 * @param element
 * @return
 */
public static List forceForRemove1(List list, Object element) {
    for (Object item : list) {
        if (item.equals(element)) {
            // 刪除後立馬終端迴圈,會正常跳出,但代價是不能繼續向後迴圈了
            list.remove(item);
            break;
        }
    }
    return list;
}

優雅刪除:使用Iterator迭代器

使用迭代器可,正確無誤的刪除,程式碼簡潔優雅,推薦使用!

 

/**
 * 使用迭代器可,正確無誤的刪除
 * 
 * @param list
 * @param element
 * @return
 */
public static List iteratorRemove(List list, Object element) {
    Iterator iterator = list.iterator();
    while (iterator.hasNext()) {
        Object cur = iterator.next();
        if (cur.equals(element)) {
            // 注意!!!這裡時Iterator.remove()!!!而不是list.remove()!!!
            iterator.remove();
        }
    }
    return list;
}

 

參考文章:

https://jingyan.baidu.com/article/7f766dafaa6ee04101e1d0e6.html

http://wiki.jikexueyuan.com/project/java-enhancement/java-thirty.html