2014-09-26 110 views
7

我是新来的Java 8,目前未能完全掌握Streams,是否可以使用Stream功能操作填充数组?这是我将如何与循环标准做一个示例代码:使用流填充多维数组

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); 
} 

如果有可能的话,使用流会怎么办?如果可能,是否方便(性能和可读性明智)?

+0

不是非常重要,但我认为你的意思'数组[X] .length'为内循环。 – 2014-09-26 04:22:20

+0

是的,我绝对没有 – 2014-09-28 17:48:08

+0

只需使用标准的for-loop。你的代码对读者来说简单明了。像流解决方案一样优雅,我不认为它在这里增加了任何东西。 – 2014-10-02 07:14:19

回答

12

这里有产生该阵列而不是修改一个预先定义的变量的溶液:

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); 

如果要使用并行流然后以避免副作用,如一个变量的修改是非常重要的(阵列或对象)。这可能会导致竞争条件或其他并发问题。您可以在java.util.stream package documentation中看到更多信息 - 请参阅非干扰,无状态行为副作用部分。

+0

其实这似乎比我发现自己的最终解决方案更复杂一些,我应该测试哪一个更快。 – 2014-11-18 23:16:51

+0

正确 - 它比其他解决方案的可读性差,但它具有好处是它不会修改外部变量当您想使用并行流时通常非常重要 - 考虑竞争条件 – 2014-11-19 13:56:59

+0

您可以在[java.util.stream](https:// docs包含文档(*非干扰*,*无状态行为*和*副作用*章节)。 – 2014-11-19 14:11:13

2

有几种方法可以做到这一点。

一种方法是使用嵌套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,而不是流。这非常适合为一维数组生成值:您提供了一个从数组索引映射到您希望在数组中分配的值的函数。例如,你可以这样做:

String[] sa = new String[17]; 
Arrays.setAll(sa, i -> letter(i)); 

不幸的是,它对多维数组不太方便。 setAll方法接受一个lambda,该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是相当不错,但外一个人必须有一个声明拉姆达调用内setAll然后返回当前数组。不太漂亮。

我不清楚这些方法是否比典型的嵌套for循环更好。

4

最好的方法是两种方法Stuart Marks’ answer的组合。

IntStream.range(0, array.length).forEach(x -> Arrays.setAll(
    array[x], y -> String.format("%c%c", letter(x), letter(y)))); 

推理导致的解决方案是“填充的多维阵列”在Java中意思是,“迭代外阵列(一个或多个)上方”接着是“填充的一维阵列”为String[][]是只是Java中的一组String[]元素。为了设置它们的元素,您必须迭代所有String[]元素,并且由于您需要索引来计算最终值,因此不能使用Arrays.stream(array).forEach(…)。所以对于外部数组迭代索引是适当的。

对于内部数组,搜索是修改(一维)数组的最佳解决方案。在这里,Arrays.setAll(…,…)是适当的。

0

工作,并围绕此进行测试后最好的选择我来了:

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))); 
    } 
} 

功能接口:

@FunctionalInterface 
public interface Object2DBuilderReturn<T> { 
    public T build2Dobject(int a, int b); 
}