Java Lambda:迭代 2 个 dim-array 保持当前索引
Java Lambda: Iterate over 2 dim-array keeping the current index
我是 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 这是一种更简洁的方法。流不是所有 "old" Java 代码的替代品。尽管如此,我还是发布了一些可能的方法。
第一种方法
这是第一种可能的方法(面向对象)。创建一个 class ArrayElement
来保存索引:
class ArrayElement<V> {
public final int row;
public final int col;
public final V elem;
...
}
然后你需要创建一个方法,从单个数组(我们将为 flatMap
调用的那个)创建一个元素流,并且 iterateAction
只打印出当前的实例
private static <T> Stream<ArrayElement<T>> 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 <V> void iterateAction(ArrayElement<V> 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]
这个解决方案的缺点是它为数组中的每个对象创建了一个新的对象。
第二种方法
第二种方法更直接,思路相同,但不需要为数组中的每个元素创建一个新的 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 <T> 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 <T> 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 的不同形式。如果你对指数不感兴趣,你可以暗示说:
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 翻译,其优点是不需要额外的 classes,但它包含两个不同的 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(e.x, e.y, e.element, e.isNull));
我是 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 这是一种更简洁的方法。流不是所有 "old" Java 代码的替代品。尽管如此,我还是发布了一些可能的方法。
第一种方法
这是第一种可能的方法(面向对象)。创建一个 class ArrayElement
来保存索引:
class ArrayElement<V> {
public final int row;
public final int col;
public final V elem;
...
}
然后你需要创建一个方法,从单个数组(我们将为 flatMap
调用的那个)创建一个元素流,并且 iterateAction
只打印出当前的实例
private static <T> Stream<ArrayElement<T>> 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 <V> void iterateAction(ArrayElement<V> 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]
这个解决方案的缺点是它为数组中的每个对象创建了一个新的对象。
第二种方法
第二种方法更直接,思路相同,但不需要为数组中的每个元素创建一个新的 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 <T> 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 <T> 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 的不同形式。如果你对指数不感兴趣,你可以暗示说:
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 翻译,其优点是不需要额外的 classes,但它包含两个不同的 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(e.x, e.y, e.element, e.isNull));