如何使用Java 8流和过滤器过滤嵌套循环?

Jav*_*Fan 3 java lambda filter java-8 java-stream

如何使用java8流和过滤器过滤嵌套循环?

假设我有一个汽车列表(List<Car>),每辆汽车都有一个引擎列表(List<Engine>),每个引擎都有一个List<Parts>。在常规Java中,此结构可以描述为:

for(Car car : cars) {
    for (Engine engine : car.getEngines()) {
        for (Part part : engine.getParts()) {
            // ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

假设我将汽车清单初始化为:

List<Car> cars =  new ArrayList<Car>(Arrays.asList(new Car(), new Car(), new Car()));
cars.get(0).setEngines(null);
cars.get(1).setEngines(new ArrayList<Engine>());
cars.get(2).setEngines(new ArrayList<Engine>() {{
    add(new Engine());
    add(null);
    add(new Engine());  
}});
Run Code Online (Sandbox Code Playgroud)

如果我想滤除的null List<Engine>,那么我会做

cars.stream().filter(p -> p.getEngines() != null).forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

如果我想过滤出列表的空数组列表,那么我会做

cars.stream().filter(p -> !p.getEngines().isEmpty()).forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

但是,如何删除Engine第3辆汽车中的空值,又如何将其他两个引擎附加到原始列表结构?换句话说,我们可以使用Java 8过滤器进入第二,第三,第n层结构,还是仅在最顶层使用过滤器?我也尝试使用.anyMatch(),没有多大运气。

为了进一步说明,考虑以下示例:我的车库中有3辆车。每辆汽车有3个引擎占位符。每个引擎都有3个占位符,用于占组成引擎的零件:

Car #1: 
 Engine#1: part1, part2, part3
 Engine#2: null, part2, empty
 Engine#3: part1, null, part3
Car #2: 
 Engine#1: part1, part2, part3
 empty:    null, null, null
 null:     null, null, null
Car #3: 
 Engine#1: null, empty, part3
 Engine#2: null, part2, empty
 Engine#3: part1, null, part3
Run Code Online (Sandbox Code Playgroud)

问题:我们如何使用Java 8 .filter,以便在过滤后获得以下信息:

Car #1: 
 Engine#1: part1, part2, part3
 Engine#2: part2, 
 Engine#3: part1, part3
Car #2: 
 Engine#1: part1, part2, part3
Car #1: 
 Engine#1: part3
 Engine#2: part2,
 Engine#3: part1,part3
Run Code Online (Sandbox Code Playgroud)

=======================

另一个例子

伙计们,我希望我刚才制作的这个例子更清楚:..本质上与上面相同,只是更为冗长,我们可以考虑使用替代汽车的方法来最大程度地减少抽象。为简洁起见,我将所有字段都公开,希望您不要介意。

假设我在“银行钱包”
Bank#1中隶属于4家银行:
我实际上是在这里银行。我被迫拥有3个帐户,但只有2个装有一些现金,而第3个帐户尚未开设(即为空)。
银行2:
我打算在这里银行。创建了帐户支持结构(空ArrayList),但未添加任何帐户。
银行#3:
我填写了一些营销表格。他们在我的CRM中拥有我,但是永远都不会开设任何帐户。
银行4:
这家银行被烧毁了,钱包中有一个工件占位符,为空。

以下代码对此进行了描述:

public class Bank_Wallet {

    public static void main(String[] args) {
        List<Bank> banks = new ArrayList<Bank>(Arrays.asList(new Bank(), new Bank(), new Bank(), null));
        // 1st bank with physical accounts, but one of them is null
        banks.get(0).accounts = Arrays.asList(new Account(), null, new Account());
        // 2nd bank with empty accounts  
        banks.get(1).accounts = new ArrayList<Account>();

        System.out.println("RAW original");
        banks.stream().forEach(System.out::println);

        System.out.println("\nFiltered result...   ");
        banks.stream()// get stream
                .filter(p -> p != null) // get rid of null banks
                .filter(p -> p.accounts != null) // get rid of null accounts
                .filter(p -> !p.accounts.isEmpty()) // get rid of empty accounts
                // .filter(p->p.accounts. ?????? ??) ?? how do I remove null account from the remaining bank entry?
                .forEach(System.out::println);

    }// main
}
Run Code Online (Sandbox Code Playgroud)


支持类在这里:

public class Bank {
    public String name;
    public static int counter = 0;
    public List<Account> accounts;

    public Bank() {
        this.name = "Bank: #" + Bank.counter++;
    }

    @Override
    public String toString() {
        return "Bank [name=" + this.name + ", accounts=" + this.accounts + "]";
    }

public class Account {
    public String name;
    public static int counter;

    public Account() {
        this.name = "Account: " + Account.counter++;
    }

    @Override
    public String toString() {
        return "Account [name=" + this.name + "]";
    }

}
Run Code Online (Sandbox Code Playgroud)

当您运行此代码时,您将看到在建议过滤后剩下的所有内容是

Bank [name=Bank: #0, accounts=[Account [name=Account: 0], null, Account [name=Account: 1]]]
Run Code Online (Sandbox Code Playgroud)

问题:我需要将其他哪些过滤器添加到代码中,以得到上述结果,但在帐户中不显示空值,而保留总体结构(银行->帐户->等->等)

Bank [name=Bank: #0, accounts=[Account [name=Account: 0], Account [name=Account: 1]]]
Run Code Online (Sandbox Code Playgroud)

And*_*eas 6

相当于

for(Car car : cars) {
    for (Engine engine : car.getEngines()) {
        for (Part part : engine.getParts()) {
            // ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

cars.stream()
    .flatMap(car -> car.getEngines().stream())
    .flatMap(engine -> engine.getParts().stream())
    .forEach(part -> { /* ... */ });
Run Code Online (Sandbox Code Playgroud)

...但是,该代码将无法访问carengine

要检查是否为空,可以在以下两个地方检查:

cars.stream()
    .flatMap(car -> car.getEngines().stream())
    .filter(engine -> engine != null)
    .flatMap(engine -> engine.getParts().stream())
    .forEach(part -> { /* ... */ });
Run Code Online (Sandbox Code Playgroud)

要么

cars.stream()
    .flatMap(car -> car.getEngines()
                       .stream()
                       .filter(engine -> engine != null))
    .flatMap(engine -> engine.getParts().stream())
    .forEach(part -> { /* ... */ });
Run Code Online (Sandbox Code Playgroud)


Dic*_*ici 4

为什么你不简单地写这个呢?

cars.stream()
    .filter(car -> notEmpty(car.getEngines()))
    .filter(car -> car.getEngines().stream().allMatch(engine -> notEmpty(engine.getParts())))
    .forEach(System.out::println);

public static <T> boolean notEmpty(Collection<T> collection) {
   return collection != null && !collection.isEmpty();
}
Run Code Online (Sandbox Code Playgroud)

  • `getEngines()` 返回 `List&lt;Engine&gt;` (或类似的),它没有 `getParts`。 (3认同)