Java 使用流填充多维数组
我是Java8新手,目前无法完全掌握流,是否可以使用流函数操作填充数组?这是一个示例代码,说明如何使用标准for循环执行此操作:Java 使用流填充多维数组,java,arrays,functional-programming,java-8,java-stream,Java,Arrays,Functional Programming,Java 8,Java Stream,我是Java8新手,目前无法完全掌握流,是否可以使用流函数操作填充数组?这是一个示例代码,说明如何使用标准for循环执行此操作: public static void testForLoop(){ String[][] array = new String[3][3]; for (int x = 0; x < array.length; x++){ for (int y = 0; y < array[x].length; y++){
public static void testForLoop(){
String[][] array = new String[3][3];
for (int x = 0; x < array.length; x++){
for (int y = 0; y < array[x].length; y++){
array[x][y] = String.format("%c%c", letter(x), letter(y));
}
}
}
public static char letter(int i){
return letters.charAt(i);
}
publicstaticvoidtestforloop(){
字符串[][]数组=新字符串[3][3];
对于(int x=0;x
如果可能的话,我将如何使用Stream?如果可能,是否方便(性能和可读性方面)?有几种方法可以做到这一点 一种方法是在行和列索引上使用一对嵌套的
IntStreams
:
String[][] testStream() {
String[][] array = new String[3][3];
IntStream.range(0, array.length).forEach(x ->
IntStream.range(0, array[x].length).forEach(y ->
array[x][y] = String.format("%c%c", letter(x), letter(y))));
return array;
}
另一种似乎很有希望的方法是使用Array.setAll
而不是streams。这对于为一维数组生成值非常有用:您提供了一个函数,该函数可以从数组索引映射到要在数组中指定的值。例如,您可以这样做:
String[] sa = new String[17];
Arrays.setAll(sa, i -> letter(i));
不幸的是,多维数组不太方便。采用lambda的setAll
方法,该lambda返回分配给该索引处数组位置的值。如果已创建多维数组,则较高维度已使用较低维度数组初始化。您不想分配给它们,但确实需要setAll
的隐式循环行为
记住这一点,您可以使用setAll
初始化多维数组,如下所示:
static String[][] testArraySetAll() {
String[][] array = new String[3][3];
Arrays.setAll(array, x -> {
Arrays.setAll(array[x], y -> String.format("%c%c", letter(x), letter(y)));
return array[x];
});
return array;
}
内部的setAll
相当不错,但是外部的必须有一个lambda语句来调用内部的setAll
,然后返回当前数组。不太漂亮
我不清楚这两种方法是否比典型的嵌套for循环更好。最好的方法是将这两种方法结合起来 导致该解决方案的原因是,Java中的“填充多维数组”意味着“在外部数组上迭代”,然后“填充一维数组”,如
String[][]
在Java中只是String[]
元素的数组。为了设置它们的元素,您必须迭代所有String[]
元素,因为您需要索引来计算最终值,所以不能使用Arrays.stream(array.forEach(…)
。因此,对于外部数组,遍历索引是合适的
对于内部数组,搜索的是修改(一维)数组的最佳解决方案。这里,
数组。setAll(…,…)
是合适的。这里有一个解决方案,可以生成数组,而不是修改以前定义的变量:
String[][] array =
IntStream.range(0, 3)
.mapToObj(x -> IntStream.range(0, 3)
.mapToObj(y -> String.format("%c%c", letter(x), letter(y)))
.toArray(String[]::new))
.toArray(String[][]::new);
如果您想使用并行流,那么避免诸如修改变量(数组或对象)之类的副作用是非常重要的。它可能会导致竞争条件或其他并发问题。您可以在中了解更多信息-请参阅“无干扰、无状态行为和副作用”部分。在围绕这一点进行工作和测试后,这是我的最佳选择:
IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> builder.build2Dobject(x, y)));
(在具体情况下,我建议:
IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> String.format("%c%c", letter(x), letter(y)));
对于3d阵列,它只是:
IntStream.range(0, array.length).forEach(x -> IntStream.range(0, array[x].length).forEach(y -> Arrays.setAll(array[x][y], z -> builder.build3Dobject(x, y, z))));
这是让程序选择最快选项的代码:
public static void fill2DArray(Object[][] array, Object2DBuilderReturn builder){
int totalLength = array.length * array[0].length;
if (totalLength < 200){
for(int x = 0; x < array.length; x++){
for (int y = 0; y < array[x].length; y++){
array[x][y] = builder.build2Dobject(x, y);
}
}
} else if (totalLength >= 200 && totalLength < 1000){
IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> builder.build2Dobject(x, y)));
} else {
IntStream.range(0, array.length).forEach(x -> Arrays.setAll(array[x], y -> builder.build2Dobject(x, y)));
}
}
publicstaticvoidfill2darray(Object[][]数组,object2dbuilderreturnbuilder){
int totalLength=array.length*数组[0]。长度;
如果(总长度<200){
对于(int x=0;x=200和总长度<1000){
IntStream.range(0,array.length).forEach(x->array.setAll(array[x],y->builder.build2Dobject(x,y));
}否则{
IntStream.range(0,array.length).forEach(x->array.setAll(array[x],y->builder.build2Dobject(x,y));
}
}
功能界面:
@FunctionalInterface
public interface Object2DBuilderReturn<T> {
public T build2Dobject(int a, int b);
}
@functioninterface
公共接口Object2DBuilderReturn{
公共T build2Dobject(inta、intb);
}
不太相关,但我想你指的是array[x].length
用于内部循环。是的,我确实使用了循环的标准。您的代码对读者来说简单明了。尽管streams解决方案看起来很优雅,但我看不到它在这里添加了任何内容。@SaintHill,因为实际的代码使用了一个包含数万到数十万个不同对象的3d数组,我想测试流方法比循环的标准方法快:)稍微快一点,阵列的总大小优于正常流的200(这可能取决于处理器)。速度更快,总大小超过1000个并行流。(我使用的是QuadCore i5-3570k,64位实际上,这似乎比我自己找到的最终解决方案更复杂,但我应该测试哪一个更快。对-它的可读性不如其他解决方案,但它的优点是它不会修改外部变量,这在使用并行流时通常非常重要-很薄您可以在包文档(非干扰、无状态行为和副作用部分)中阅读更多关于这方面的内容。我正在试图理解我在这里缺少的内容。Arrays.setAll不是.mapToObj(…).toArray(…).toArray(…)的快捷方式吗?@AngeloAlvisi No,Arrays.setAll正在迭代传递的数组和给定函数生成的设置值。请查看源代码:
@FunctionalInterface
public interface Object2DBuilderReturn<T> {
public T build2Dobject(int a, int b);
}