如何使用java8流和过滤器过滤嵌套循环?
假设我有一个汽车列表(List<Car>
),每辆汽车都有一个引擎列表(List<Engine>
),每个引擎都有一个List<Parts>
。
在常规Java中,这种结构可以描述为:
for(Car car : cars) {
for (Engine engine : car.getEngines()) {
for (Part part : engine.getParts()) {
// ...
}
}
}
假设我将汽车列表初始化为:
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());
}});
如果我想过滤掉List<Engine>
的空值,那么我会做
cars.stream().filter(p -> p.getEngines() != null).forEach(System.out::println);
如果我想过滤掉List的空arraylists,那么我会做
cars.stream().filter(p -> !p.getEngines().isEmpty()).forEach(System.out::println);
但是如何删除第3辆车中的null Engine
并将其他两个引擎连接到原始列表结构?换句话说,我们可以使用Java 8过滤器进入第2级,第3级,第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
问题:我们如何使用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
=======================
伙计们,我希望我刚刚编写的这个例子更清楚:......基本上它与上面相同只是它更冗长而不是汽车我们可以想到银行来减少抽象。为了简明起见,我将所有领域公之于众,我希望你不要介意。
假设我与我的“银行钱包”中的4家银行有关联
银行#1:
我在这里实际存钱。我被迫有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
}
支持类在这里:
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 + "]";
}
}
当你运行这段代码时,你会看到建议过滤之后我剩下的就是左
Bank [name=Bank: #0, accounts=[Account [name=Account: 0], null, Account [name=Account: 1]]]
问题: 我需要做什么其他过滤器添加到代码中以使上述结果在帐户中不显示null并保持整体结构(Bank-&gt; Account-&gt; etc-&gt; etc)
Bank [name=Bank: #0, accounts=[Account [name=Account: 0], Account [name=Account: 1]]]
答案 0 :(得分:4)
相当于
的流for(Car car : cars) {
for (Engine engine : car.getEngines()) {
for (Part part : engine.getParts()) {
// ...
}
}
}
是
cars.stream()
.flatMap(car -> car.getEngines().stream())
.flatMap(engine -> engine.getParts().stream())
.forEach(part -> { /* ... */ });
...
代码无法访问car
和engine
。
要检查null,您可以在两个地方办理登机手续:
cars.stream()
.flatMap(car -> car.getEngines().stream())
.filter(engine -> engine != null)
.flatMap(engine -> engine.getParts().stream())
.forEach(part -> { /* ... */ });
或
cars.stream()
.flatMap(car -> car.getEngines()
.stream()
.filter(engine -> engine != null))
.flatMap(engine -> engine.getParts().stream())
.forEach(part -> { /* ... */ });
答案 1 :(得分:2)
你为什么不写这个?
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();
}
答案 2 :(得分:1)
如何关注?
List<Car> cars = new ArrayList<Car>(Arrays.asList(new Car("C0"), new Car("C1"), new Car("C2")));
cars.get(0).setEngines(new ArrayList<Engine>());
cars.get(1).setEngines(new ArrayList<Engine>());
cars.get(2).setEngines(Arrays.asList(new Engine("C2E1"), new Engine("C2E2"), null));
cars.stream().filter(c -> Objects.nonNull(c.getEngines())).forEach(c -> {
System.out.printf("Car %s ", c);
c.getEngines().stream().filter(e -> Objects.nonNull(e) && Objects.nonNull(e.getParts())).forEach(e -> {
System.out.printf(" Engine %s ", e);
e.getParts().stream().filter(p -> Objects.nonNull(p))
.forEach(p -> System.out.printf("Part %s", p));
});
System.out.println();
});
产生以下内容:
Car C0
Car C1
Car C2 Engine C2E1 Part DefaultPart Engine C2E2 Part DefaultPart
为Car / Engine / Part类覆盖“toString”。
希望这有帮助。
答案 3 :(得分:0)
您可以使用removeIf()
,如下所示:
cars.removeIf(car -> car.getEngines() == null);
cars.forEach(c->System.out.println("Car :"+c);