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

集合在这种遍历时被修改会“很不高兴”——迭代会被破坏。

如何正确地从集合中删除元素?

方法 1:使用 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() 删除。

方法 2:创建只包含需要元素的新列表

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]

我们不去修改原始集合,而是重新组装一个新列表。这个方法安全、直观,并且在条件变复杂时也容易扩展。

方法 3:使用 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()——最为简洁且表达力强。

示例:有一个单词列表,需要删除所有长度小于 4 的单词:

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 方法接收谓词(过滤规则),并删除所有满足该规则的元素。

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”,并使用 <=,那么 0 也会被删除:

numbers.removeIf(n -> n <= 0); // 结果: [2, 4] —— 0 被误删

请务必确保条件精确:一个错误的比较就可能完全改变结果。

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION