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

How do you filter nested loops using java8 streams and filters?

Suppose I have a list of cars (List), each car having a list of Engines (List), each engine having a List.

In regular Java this structure can be described as:

for(Car car : cars) {

for (Engine engine : car.getEngines()) {

for (Part part : engine.getParts()) {

// ...

}

}

}

Suppose I initialise the list of cars as:

List cars = new ArrayList(Arrays.asList(new Car(), new Car(), new Car()));

cars.get(0).setEngines(null);

cars.get(1).setEngines(new ArrayList());

cars.get(2).setEngines(new ArrayList() {{

add(new Engine());

add(null);

add(new Engine());

}});

If I want to filter out nulls of List, then I would do

cars.stream().filter(p -> p.getEngines() != null).forEach(System.out::println);

If I want to filter out empty arraylists of List, then I would do

cars.stream().filter(p -> !p.getEngines().isEmpty()).forEach(System.out::println);

But how do I remove the null Engine in a 3rd car and yet keep two other engines attached to the original list structure? In other words, can we go into the 2nd, 3rd, nth level of hierarchy with Java 8 filters or do filters only work on the top-most layer? I also tried to use .anyMatch(), without much luck.

just to further clarify, consider the following example:

I have 3 cars in my garage. Each car has 3 placeholders for engine. Each engine has 3 placeholders for parts that make up the engine:

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

Question: how do we use Java 8 .filter, such that when after filtering I get the following:

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

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

Another example

Guys I hope this example that I just made up is clearer:.. Essentially it is the same as above only it is more verbose and instead of cars we can think of banks to minimize abstraction. For conciseness I make all fields public, I hope you don't mind.

Suppose I am affiliated with 4 banks in my 'bank wallet'

Bank#1:

I physically bank here. I am forced to have 3 accounts, but only are 2 filled with some cash and 3rd is yet to opened (ie null)

Bank #2:

I plan to bank here. Account support structure is created (empty ArrayList), but no accounts are added

Bank #3:

I filled out some marketing form. They have me in their CRM but no accounts will ever be opened

Bank #4:

This bank burned down, there is an artifact placeholder in the wallet, which is null.

The following code describes this:

public class Bank_Wallet {

public static void main(String[] args) {

List banks = new ArrayList(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();

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

}

The support classes are here:

public class Bank {

public String name;

public static int counter = 0;

public List 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 + "]";

}

}

when you run this code you will see that after suggested filtering all I am left with is

Bank [name=Bank: #0, accounts=[Account [name=Account: 0], null, Account [name=Account: 1]]]

Question:

What other filter do I need do add to the code to get the above result not show null in the accounts and yet retain the overall structure (Bank->Account->etc->etc)

Bank [name=Bank: #0, accounts=[Account [name=Account: 0], Account [name=Account: 1]]]

解决方案

The stream equivalent of

for(Car car : cars) {

for (Engine engine : car.getEngines()) {

for (Part part : engine.getParts()) {

// ...

}

}

}

is

cars.stream()

.flatMap(car -> car.getEngines().stream())

.flatMap(engine -> engine.getParts().stream())

.forEach(part -> { /* ... */ });

The ... code will however not have access to car and engine.

To check for null, you can check in two places:

cars.stream()

.flatMap(car -> car.getEngines().stream())

.filter(engine -> engine != null)

.flatMap(engine -> engine.getParts().stream())

.forEach(part -> { /* ... */ });

or

cars.stream()

.flatMap(car -> car.getEngines()

.stream()

.filter(engine -> engine != null))

.flatMap(engine -> engine.getParts().stream())

.forEach(part -> { /* ... */ });

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值