Si consideri il seguente classe:ricorsiva uso di Stream.flatMap()
public class Order {
private String id;
private List<Order> orders = new ArrayList<>();
@Override
public String toString() {
return this.id;
}
// getters & setters
}
NOTA: E 'importante notare che io non riesco a modificare questa classe, perché sto consumando da una esterna API.
Considera anche la seguente gerarchia di ordini:
Order o1 = new Order();
o1.setId("1");
Order o11 = new Order();
o11.setId("1.1");
Order o111 = new Order();
o111.setId("1.1.1");
List<Order> o11Children = new ArrayList<>(Arrays.asList(o111));
o11.setOrders(o11Children);
Order o12 = new Order();
o12.setId("1.2");
List<Order> o1Children = new ArrayList<>(Arrays.asList(o11, o12));
o1.setOrders(o1Children);
Order o2 = new Order();
o2.setId("2");
Order o21 = new Order();
o21.setId("2.1");
Order o22 = new Order();
o22.setId("2.2");
Order o23 = new Order();
o23.setId("2.3");
List<Order> o2Children = new ArrayList<>(Arrays.asList(o21, o22, o23));
o2.setOrders(o2Children);
List<Order> orders = new ArrayList<>(Arrays.asList(o1, o2));
Che potrebbe essere rappresentato visivamente in questo modo:
1
1.1
1.1.1
1.2
2
2.1
2.2
2.3
Ora, voglio appiattire questa gerarchia di ordini in un List
, in modo che Prendo il seguente:
[1, 1.1, 1.1.1, 1.2, 2, 2.1, 2.2, 2.3]
Sono riuscito a farlo ricorsivamente usando flatMap()
(insieme ad una classe di supporto), come segue:
List<Order> flattened = orders.stream()
.flatMap(Helper::flatten)
.collect(Collectors.toList());
Questa è la classe helper:
public final class Helper {
private Helper() {
}
public static Stream<Order> flatten(Order order) {
return Stream.concat(
Stream.of(order),
order.getOrders().stream().flatMap(Helper::flatten)); // recursion here
}
}
La seguente riga:
System.out.println(flattened);
produce la seguente output:
[1, 1.1, 1.1.1, 1.2, 2, 2.1, 2.2, 2.3]
Fin qui tutto bene. Il risultato è assolutamente corretto.
Tuttavia, after reading this question, ho avuto alcune preoccupazioni riguardanti l'utilizzo di flatMap()
all'interno di un metodo ricorsivo. In particolare, volevo sapere come si espandeva il flusso (se è il termine). Così ho modificato la classe Helper
e utilizzato peek(System.out::println)
di controllare questo:
public static final class Helper {
private Helper() {
}
public static Stream<Order> flatten(Order order) {
return Stream.concat(
Stream.of(order),
order.getOrders().stream().flatMap(Helper::flatten))
.peek(System.out::println);
}
}
e la produzione ammontava:
1
1.1
1.1
1.1.1
1.1.1
1.1.1
1.2
1.2
2
2.1
2.1
2.2
2.2
2.3
2.3
Non sono sicuro se questo è l'uscita che deve essere stampato.
Quindi, mi chiedo se è OK lasciare che i flussi intermedi contengano elementi ripetuti. Inoltre, quali sono i pro e i contro di questo approccio? Dopotutto, è corretto utilizzare flatMap()
in questo modo? C'è un modo migliore per ottenere lo stesso?
Solo curioso: perché si imposta l'id dopo aver creato l'ordine anziché renderlo un argomento al costruttore? – sprinter
@sprinter Non posso modificare la classe 'Order' dato che è parte di un'API che sto consumando. –
Ah, capisco. Quindi la maggior parte della mia risposta è abbastanza inutile. Potrebbe valere la pena aggiungere quell'informazione alla tua domanda. – sprinter