
在Java Stream API中,我们经常需要对对象集合进行过滤操作。一个常见的需求是,根据对象的某个特定属性(例如,Animal对象的color属性)来筛选流,但最终过滤出的结果仍然是完整的原始对象(即Animal对象本身),而不是该属性的值。
例如,我们有一个Animal对象的流animalStream,希望筛选出所有颜色为绿色的动物。直观上可能会想到以下两种方式:
直接使用filter与方法引用:
Stream<Animal> swimmingAnimalStream = animalStream
    .filter(Animal::canSwim); // 过滤所有会游泳的动物这种方式简单明了,当过滤条件可以直接通过方法引用表达时非常高效。
立即学习“Java免费学习笔记(深入)”;
使用filter与Lambda表达式:
Stream<Animal> greenAnimals = animalStream
    .filter(animal -> animal.getColor().equals(Colors.GREEN)); // 过滤所有颜色为绿色的动物当过滤条件稍微复杂,需要访问对象内部属性并进行比较时,Lambda表达式提供了极大的灵活性。
然而,有时开发者可能希望在过滤前先“映射”出需要判断的属性,然后再进行过滤,但又不想丢失原始对象的信息。例如,以下操作虽然可以过滤出绿色,但最终流中只剩下颜色信息,而非原始的Animal对象:
animalStream
    .map(Animal::getColor) // 将Animal映射为Colors
    .filter(Colors.GREEN::equals); // 过滤颜色为绿色的Colors,结果流为Stream<Colors>这种方式会改变流中元素的类型,不符合“保留原始完整对象”的需求。
为了解决这一问题,同时避免提取辅助方法,Java Stream API提供了多种策略。
对于大多数基于对象内部属性进行过滤的需求,最简洁、最符合Stream声明式风格的解决方案是直接在filter操作中使用Lambda表达式。这种方法将属性的获取和条件的判断逻辑封装在一个Lambda中,确保了流的类型不变。
示例代码:
import java.util.List;
import java.util.stream.Stream;
// 假设有Animal类和Colors枚举
class Animal {
    enum Colors { GREEN, BLUE, RED }
    private Colors color;
    private String name;
    // ... 构造函数, getter等
    public Colors getColor() { return color; }
    public String getName() { return name; }
    public Animal(String name, Colors color) {
        this.name = name;
        this.color = color;
    }
    @Override
    public String toString() {
        return "Animal{name='" + name + "', color=" + color + '}';
    }
}
public class StreamFilterExample {
    public static void main(String[] args) {
        List<Animal> animals = List.of(
            new Animal("Frog", Animal.Colors.GREEN),
            new Animal("Parrot", Animal.Colors.BLUE),
            new Animal("Chameleon", Animal.Colors.GREEN),
            new Animal("Crab", Animal.Colors.RED)
        );
        Stream<Animal> animalStream = animals.stream();
        // 过滤颜色为绿色的动物,并保留原始Animal对象
        Stream<Animal> greenAnimalsStream = animalStream
            .filter(animal -> Animal.Colors.GREEN.equals(animal.getColor()));
        greenAnimalsStream.forEach(System.out::println);
        // 输出:
        // Animal{name='Frog', color=GREEN}
        // Animal{name='Chameleon', color=GREEN}
    }
}优点:
适用场景: 当过滤条件可以通过一个简单的Lambda表达式直接访问对象属性并进行判断时,此方法是首选。
对于更复杂、需要精细控制元素发射,或者希望在单个中间操作中完成映射和过滤的场景,Java 16引入的Stream#mapMulti提供了一个强大的替代方案。mapMulti允许你根据任意逻辑,将输入流中的每个元素转换为零个、一个或多个输出元素。这使得它非常适合实现条件性地“接受”或“拒绝”原始对象的需求。
mapMulti方法接收一个BiConsumer<T, Consumer<R>>作为参数。T是输入流的元素类型,R是输出流的元素类型。在BiConsumer的实现中,你可以通过第二个参数Consumer<R>来决定是否以及如何将元素发射到下游流。
示例代码:
import java.util.List;
import java.util.stream.Stream;
import java.util.Optional;
// 假设有Animal类,并增加getMother方法
class Animal {
    enum Colors { GREEN, BLUE, RED }
    private Colors color;
    private String name;
    private Optional<Animal> mother; // 假设动物可能有母亲
    public Animal(String name, Colors color) {
        this(name, color, Optional.empty());
    }
    public Animal(String name, Colors color, Optional<Animal> mother) {
        this.name = name;
        this.color = color;
        this.mother = mother;
    }
    public Colors getColor() { return color; }
    public Optional<Animal> getMother() { return mother; }
    public String getName() { return name; }
    @Override
    public String toString() {
        return "Animal{name='" + name + "', color=" + color + ", hasMother=" + mother.isPresent() + '}';
    }
}
public class StreamMapMultiFilterExample {
    public static void main(String[] args) {
        Animal motherAnimal = new Animal("Mama", Animal.Colors.BLUE);
        List<Animal> animals = List.of(
            new Animal("Frog", Animal.Colors.GREEN),
            new Animal("Parrot", Animal.Colors.BLUE),
            new Animal("Chameleon", Animal.Colors.GREEN, Optional.of(motherAnimal)),
            new Animal("Crab", Animal.Colors.RED)
        );
        Stream<Animal> animalStream = animals.stream();
        // 使用mapMulti过滤颜色为绿色的动物
        Stream<Animal> greenAnimalsMapMultiStream = animalStream
            .mapMulti((animal, consumer) -> {
                if (Animal.Colors.GREEN.equals(animal.getColor())) { // 条件判断
                    consumer.accept(animal); // 如果满足条件,则将原始Animal对象发射到下游
                }
            });
        greenAnimalsMapMultiStream.forEach(System.out::println);
        // 输出:
        // Animal{name='Frog', color=GREEN, hasMother=false}
        // Animal{name='Chameleon', color=GREEN, hasMother=true}
        System.out.println("\n--- 过滤有母亲的动物 ---");
        Stream<Animal> animalWithMotherStream = animals.stream()
            .mapMulti((animal, consumer) -> {
                if (animal.getMother().isPresent()) { // 检查是否有母亲
                    consumer.accept(animal); // 如果有,发射原始Animal对象
                }
            });
        animalWithMotherStream.forEach(System.out::println);
        // 输出:
        // Animal{name='Chameleon', color=GREEN, hasMother=true}
    }
}Stream#mapMulti的优点:
Stream#mapMulti的缺点:
使用场景:
在Java Stream中根据对象的某个属性进行过滤并保留原始对象,主要有两种策略:
选择哪种方法取决于您的具体需求、Java版本以及对代码可读性和性能优化的权衡。对于日常开发中的常规过滤任务,filter操作通常是最佳选择。
以上就是Java Stream 高级过滤技巧:基于映射值保留原始对象的详细内容,更多请关注php中文网其它相关文章!
 
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
 
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号