CodeGym /課程 /JAVA 25 SELF /集合元素的篩選

集合元素的篩選

JAVA 25 SELF
等級 28 , 課堂 0
開放

1. 前言

在程式設計中,我們經常需要從大量資料中只挑出需要的元素——這就是篩選。想要只保留偶數、找出包含 "Java" 一詞的字串,或是選出年齡大於 18 歲的使用者——這些都是篩選的任務。

Java 中,這類操作隨處可見,因此熟悉各種做法並理解其差異非常重要。

2. 用迴圈進行篩選

我們先從最基本的方法——一般的 for 迴圈。這種做法稱為命令式,因為你會明確地指示要做什麼以及怎麼做。它可讀性高,易於理解。

範例:只保留偶數

import java.util.*;

public class FilterExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        List<Integer> evenNumbers = new ArrayList<>(); // 建立一個新的清單用來存放結果

        for (Integer n : numbers) {
            if (n % 2 == 0) { // 檢查條件:偶數
                evenNumbers.add(n);
            }
        }

        System.out.println("偶數: " + evenNumbers);
    }
}

結果:

偶數: [2, 4, 6, 8, 10]

這裡要記住:原始集合(numbers不會改變。我們是建立一個新的結果清單。

範例:依子字串篩選字串

List<String> words = Arrays.asList("java", "python", "javascript", "kotlin", "c++");
List<String> javaWords = new ArrayList<>();

for (String word : words) {
    if (word.contains("java")) {
        javaWords.add(word);
    }
}
System.out.println(javaWords); // [java, javascript]

同樣的原理:遍歷字串清單,並用 contains 方法檢查是否包含子字串。

3. 從集合中刪除元素:為什麼沒那麼簡單?

陷阱在哪裡?

有時我們想刪除原始集合中所有不需要的元素。但如果在 for-each 迴圈遍歷時這麼做,你會得到例外 ConcurrentModificationException

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));

for (Integer n : numbers) {
    if (n < 0) {
        numbers.remove(n); // 危險!ConcurrentModificationException!
    }
}

結果:

Exception in thread "main" java.util.ConcurrentModificationException

集合在這種遍歷過程中被修改會導致問題——迭代會失效。

如何正確地從集合中刪除元素?

方法一:使用 Iterator.remove()

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));

Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
    Integer n = it.next();
    if (n < 0) {
        it.remove(); // 安全刪除!
    }
}
System.out.println(numbers); // [1, 3, 5]

在這裡我們透過 iterator() 取得迭代器,使用 hasNext()next() 在集合中移動,並用 it.remove() 刪除不需要的元素。

方法二:建立只包含需要元素的新清單

List<Integer> numbers = Arrays.asList(1, -2, 3, -4, 5);
List<Integer> positive = new ArrayList<>();

for (Integer n : numbers) {
    if (n >= 0) {
        positive.add(n);
    }
}
System.out.println(positive); // [1, 3, 5]

我們不動原始集合,改為收集到新的清單。這種做法安全、直觀,且在條件變複雜時也容易擴充。

方法三:使用 removeIf(Java 8+)

List<Integer> numbers = new ArrayList<>(Arrays.asList(1, -2, 3, -4, 5));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [1, 3, 5]

只用一行就能傳入條件:「刪掉所有小於 0 的元素」。至於如何安全地修改,集合會在內部替你處理。

總結:要刪除元素時不要使用 for-each。請在 Iterator.remove()、建立新清單,或精煉的 removeIf 之間做選擇。

4. 什麼時候用哪種篩選方式?

透過一般迴圈的命令式做法很適合在你不僅要篩選,還要立即對元素做些處理時(例如輸出或轉換)。它簡單且透明。

若是要從原始集合中刪除元素,請避免在 for-each 中刪除。需要逐步控制時使用 Iterator.remove(),或者採用現代的 removeIf()——最簡短且表意清楚的方式。

範例:有一個字詞清單,需要刪除所有少於四個字元的字詞:

List<String> words = new ArrayList<>(Arrays.asList("Java", "is", "fun", "awesome", "code"));
words.removeIf(word -> word.length() < 4);
System.out.println(words); // [Java, awesome, code]

方法 removeIf 接收一個 predicate(篩選規則),並刪除所有符合該規則的元素。

5. 集合篩選的常見錯誤

錯誤 №1:在 for-each 迴圈中嘗試從集合刪除元素。
這樣的程式會導致 ConcurrentModificationException

for (Integer n : numbers) {
    if (n < 0) {
        numbers.remove(n); // 砰!ConcurrentModificationException
    }
}

正確作法:使用 Iterator.remove()removeIf

錯誤 №2:篩選條件寫錯。
需求是只刪除負數:

List<Integer> numbers = new ArrayList<>(Arrays.asList(-3, -1, 0, 2, 4));
numbers.removeIf(n -> n < 0);
System.out.println(numbers); // [0, 2, 4]

但如果誤寫為「非正數」並使用 <=,那麼 0 也會被刪掉:

numbers.removeIf(n -> n <= 0); // 結果: [2, 4] — 0 被錯誤地刪除了

請務必精確撰寫條件:只要一個比較寫錯,結果就會完全不同。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION