In linea di principio, è possibile sostituire cicli annidati con un Stream
utilizzando flatMap
. Ciò richiede che tu scelga un tipo di elemento in grado di mantenere le informazioni equivalenti alle variabili del ciclo, se ne hai bisogno. Nel tuo caso, sono i due valori per x
e . Semplificherebbe il codice se la tua classe Node
contiene queste informazioni poiché puoi facilmente iterare sui nodi anziché sui valori int
. Dal momento che non è stato specificato le capacità della vostra classe Node
, ecco un esempio che utilizza un long[]
di dimensioni due per contenere i punti:
IntStream.range(0, width).boxed()
.flatMap(x->IntStream.range(0, height).mapToObj(y->new int[]{ x, y }))
.forEach(p1 -> {
Consumer<Node> register=getNode(p1[0], p1[1])::registerObserverAtNeighbor;
IntStream.rangeClosed(p1[0]-1, p1[0]+1).boxed()
.flatMap(x->IntStream.rangeClosed(p1[1]-1, p1[1]+1).mapToObj(y->new int[]{ x,y }))
.filter(p2 -> (p1[0] != p2[0] || p1[1] != p2[1]))
.map(point -> getNode(point[0], point[1]))
.filter(node -> node != null)
.forEach(register);
});
Semplifica ancora il codice interno spostando il codice verso l'esterno, in cui possibile, ad es le chiamate getNode
. È inoltre possibile semplificare il codice mettendo il compito ripetuta di creazione di un Stream
punti su di un'area in un metodo:
static Stream<int[]> area(int x0, int x1, int y0, int y1) {
return IntStream.range(x0, x1).boxed()
.flatMap(x->IntStream.range(y0, y1).mapToObj(y->new int[]{ x, y }));
}
quindi è possibile utilizzare in questo modo:
area(0, width, 0, height).forEach(p1 -> {
Consumer<Node> register=getNode(p1[0], p1[1])::registerObserverAtNeighbor;
area(p1[0]-1, p1[0]+2, p1[1]-1, p1[1]+2)
.filter(p2 -> (p1[0] != p2[0] || p1[1] != p2[1]))
.map(point -> getNode(point[0], point[1]))
.filter(node -> node != null)
.forEach(register);
});
E ancora potrebbe essere più facile se si ha/usa una classe di punti dedicata o se la classe di nodo contiene le informazioni sui punti (e ha un metodo di confronto per essa, nel migliore dei casi).