Como você filtra loops aninhados usando fluxos e filtros Java 8?

Como você filtra loops aninhados usando fluxos e filtros java8?

Suponha que eu tenha uma lista de carros (List<Car>), cada carro com uma lista de motores (List<Engine>), cada motor com umaList<Parts>. No Java regular, essa estrutura pode ser descrita como:

for(Car car : cars) {
    for (Engine engine : car.getEngines()) {
        for (Part part : engine.getParts()) {
            // ...
        }
    }
}

Suponha que eu inicialize a lista de carros como:

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());  
}});

Se eu quiser filtrar nulos deList<Engine>, então eu faria

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

Se eu quiser filtrar listas de matrizes vazias da Lista, eu faria

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

Mas como faço para remover o valor nuloEngine em um terceiro carro e ainda manter outros dois motores conectados à estrutura da lista original? Em outras palavras, podemos entrar no segundo, terceiro e nono nível de hierarquia com os filtros Java 8 ou os filtros funcionam apenas na camada superior? Eu também tentei usar.anyMatch(), sem muita sorte.

só para esclarecer melhor, considere o seguinte exemplo: Eu tenho 3 carros na minha garagem. Cada carro possui 3 espaços reservados para o motor. Cada mecanismo possui 3 espaços reservados para as peças que compõem o mecanismo:

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

Pergunta: como usamos o Java 8 .filter, de modo que, após a filtragem, obtenho o seguinte:

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

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

Outro exemplo

Pessoal, espero que este exemplo que acabei de criar seja mais claro: .. Essencialmente, é o mesmo que acima, apenas é mais detalhado e, em vez de carros, podemos pensar em bancos para minimizar a abstração. Por concisão, publico todos os campos, espero que não se importe.

Suponha que eu seja afiliado a 4 bancos na minha 'carteira bancária'
Banco 1:
Eu fisicamente banco aqui. Sou forçado a ter três contas, mas apenas duas estão cheias de dinheiro e a terceira ainda está aberta (ou seja, nula)
Banco 2:
Eu pretendo depositar aqui. A estrutura de suporte da conta é criada (ArrayList vazia), mas nenhuma conta é adicionada
Banco 3:
Preenchi algum formulário de marketing. Eles me têm em seu CRM, mas nenhuma conta será aberta
Banco 4:
Esse banco foi incendiado, há um espaço reservado de artefato na carteira, que é nulo.

O código a seguir descreve isso:

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
}


As classes de suporte estão aqui:

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

}

ao executar esse código, você verá que, depois de sugerir a filtragem, tudo o que me resta é

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

Pergunta: De que outro filtro preciso adicionar ao código para obter o resultado acima não mostrado nulo nas contas e ainda manter a estrutura geral (Banco-> Conta-> etc-> etc)

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

questionAnswers(4)

yourAnswerToTheQuestion