Java Lambda:迭代2个dim-array保持当前索引

我是Java 8的Lambda表达式的新手,我想制定以下内容:我有一个二维数组,我想在我的应用程序代码中迭代几次,并对数组中的项进行处理。 在我做以下之前:

public static abstract class BlaBlaIterator { private final BlaBla[][] blabla; public BlaBlaIterator(final BlaBla[][] blabla) { this.blabla = blabla; } public void iterate() { final int size = blabla.length; for (int x = 0; x < size; x++) { for (int y = 0; y < size; y++) { final BlaBla bla = blabla[x][y]; iterateAction(x, y, bla, bla == null); } } } public abstract void iterateAction(int x, int y, BlaBla bla, boolean isNull); } 

接着

  BlaBla[][] blabla = ... new BlaBlaIterator(blabla) { @Override public void iterateAction(final int x, final int y, final BlaBla bla, final boolean isNull) { //... } }.iterate(); 

至关重要的是:我需要访问当前的x / y,我需要计算像isNull这样的东西。

我现在要做的是将其转换为lambda。 我想写这样的东西:

  BlaBla[] blabla = ... blabla.stream().forEach((x, y, blabla, isNull) -> ... ); 

要从二维数组中获取流,我可以做到

  Arrays.stream(field).flatMap(x -> Arrays.stream(x)) 

但后来我松开了x / y信息,无法传递像isNull这样的计算内容。 我怎样才能做到这一点?

说实话,我会保留传统的嵌套循环,IMO这是一个更清洁的方法。 Streams不是所有“旧”Java代码的替代品。 不过,我发布了一些可能的方法。

第一种方法

这是第一种可能的方法(面向对象)。 创建一个ArrayElement类来保存索引:

 class ArrayElement { public final int row; public final int col; public final V elem; ... } 

然后你需要创建一个方法,从单个数组(我们将为flatMap调用的那个)创建元素Stream,而iterateAction只打印出当前实例

 private static  Stream> createStream(int row, T[] arr) { OfInt columns = IntStream.range(0, arr.length).iterator(); return Arrays.stream(arr).map(elem -> new ArrayElement<>(row, columns.nextInt(), elem)); } private static  void iterateAction(ArrayElement elem) { System.out.println(elem); } 

最后主要看起来像这样:

 String[][] arr = {{"One", "Two"}, {"Three", "Four"}}; OfInt rows = IntStream.range(0, arr.length).iterator(); Arrays.stream(arr) .flatMap(subArr -> createStream(rows.nextInt(), subArr)) .forEach(Main::iterateAction); 

和输出:

 ArrayElement [row=0, col=0, elem=One] ArrayElement [row=0, col=1, elem=Two] ArrayElement [row=1, col=0, elem=Three] ArrayElement [row=1, col=1, elem=Four] 

此解决方案的缺点是它为数组中的每个Object创建一个新Object。

第二种方法

第二种方法更直接,它是相同的想法,但你不为数组中的每个元素创建一个新的ArrayElement实例。 再一次,这可以在一个衬里完成,但lamdba会变得丑陋,所以我将它们分解为方法(如第一种方法):

 public class Main { public static void main(String[] args) { String[][] arr = { {"One", "Two"}, {null, "Four"}}; OfInt rows = IntStream.range(0, arr.length).iterator(); Arrays.stream(arr).forEach(subArr -> iterate(subArr, rows.nextInt())); } static  void iterate(T[] arr, int row) { OfInt columns = IntStream.range(0, arr.length).iterator(); Arrays.stream(arr).forEach(elem -> iterateAction(row, columns.nextInt(), elem, elem == null)); } static  void iterateAction(int x, int y, T elem, boolean isNull) { System.out.println(x+", "+y+", "+elem+", "+isNull); } } 

它输出:

 0, 0, One, false 0, 1, Two, false 1, 0, null, true 1, 1, Four, false 

第三种方法

使用两个AtomicInteger实例

 String[][] arr = {{"One", "Two"}, {null, "Four"}}; AtomicInteger rows = new AtomicInteger(); Arrays.stream(arr).forEach(subArr -> { int row = rows.getAndIncrement(); AtomicInteger colums = new AtomicInteger(); Arrays.stream(subArr).forEach(e -> iterateAction(row, colums.getAndIncrement(), e, e == null)); }); 

它产生与上面相同的输出。

我的结论

它可以使用Streams,但我更喜欢你的用例中的嵌套循环,因为你需要x和y值。

这是一个问题,类似于for -loop的不同forms。 如果您对指数不感兴趣,可以暗示:

 for(BlaBla[] array: blabla) for(BlaBla element: array) action(element); 

但是如果你对索引感兴趣,你不能使用for-each循环但必须迭代索引并在循环体中获取数组元素。 同样,您必须在使用Stream时流式传输索引并需要索引:

 IntStream.range(0, blabla.length) .forEach(x -> IntStream.range(0, blabla[x].length) .forEach(y -> { final BlaBla bla = blabla[x][y]; iterateAction(x, y, bla, bla == null); }) ); 

这是1:1的转换,其优点是不需要额外的类,但它包括两个不同的Stream操作而不是一个融合操作,这是优选的。

单个融合操作可能如下所示:

帮助class:

 class ArrayElement { final int x, y; BlaBla element; final boolean isNull; ArrayElement(int x, int y, BlaBla obj) { this.x=x; this.y=y; element=obj; isNull=obj==null; } } 

实际操作:

 IntStream.range(0, blabla.length).boxed() .flatMap(x -> IntStream.range(0, blabla[x].length) .mapToObj(y->new ArrayElement(x, y, blabla[x][y]))) .forEach(e -> iterateAction(ex, ey, e.element, e.isNull));