Arrays.fill 在 Java 中使用多维数组

Posted

技术标签:

【中文标题】Arrays.fill 在 Java 中使用多维数组【英文标题】:Arrays.fill with multidimensional array in Java 【发布时间】:2011-10-30 09:31:56 【问题描述】:

如何在不使用循环的情况下在 Java 中填充多维数组?我试过了:

double[][] arr = new double[20][4];
Arrays.fill(arr, 0);

这导致java.lang.ArrayStoreException: java.lang.Double

【问题讨论】:

@Caroline:如果您尝试用 0 初始化 2d 数组,则无需这样做,因为在分配数组时它已经用 0 初始化,并且您无法在不使用 a 的情况下初始化任何数组loop.你可以像 Arrays.fill 一样在函数中隐藏循环。 嘿伙计们不要夸大其词了。他想要的只是 Java API 中的一些方法,可以一步将多维数组初始化为某个默认值。这就是他想说的没有循环。 @Number945 我相信 Caroline 是一个女性名字。 【参考方案1】:

递归解决方案

用任何给定值填充二维数组的每一行的简单递归解决方案。

double[][] arr = new double[20][4];
int n=arr.length;
fillArrRecursively(arr, n-1, 10); //Recursion call

//Recursive method to fill every row of 'arr' with the passed variable 'val'

public static int fillArrRecursively(double arr[][], int n, int val)
    if(n<0) return 1;
    Arrays.fill(arr[n], val);
    return fillArrRecursively(arr, n-1, val);

【讨论】:

你至少应该为你的答案添加一些解释。【参考方案2】:

Arrays.fill 仅适用于一维数组

java.util.Arrays 的来源:

public static void fill(Object[] a, Object val) 
        int i = 0;

        for(int len = a.length; i < len; ++i) 
            a[i] = val;
        

使用自己的循环初始化数组

【讨论】:

【参考方案3】:

Arrays.fill 适用于一维数组,因此我们可以在下面进行填充二维数组

for (int i = 0, len = arr.length; i < len; i++)
    Arrays.fill(arr[i], 0);

【讨论】:

【参考方案4】:

根据 Java 8,我们可以使用这种方式。

double[][] arr = new double[20][4]; Arrays.stream(arr).forEach(a -> Arrays.fill(a, 0));

我们可以用一种更好更智能的方式初始化多维数组中的值。

【讨论】:

从可读性的角度来看,我觉得这并不比 for 循环好。 @JianwuChen OP 要求在没有循环的情况下填充数组 @JianwuChen 实际上可读性在某种程度上取决于背景:来自 scala 这感觉很自然。【参考方案5】:

简而言之,java 不提供这样的 API。 您需要遍历循环并使用填充方法可以用一个循环填充二维数组。

      int row = 5;
      int col = 6;
      int cache[][]=new int[row][col];
      for(int i=0;i<=row;i++)
          Arrays.fill(cache[i]);
      

【讨论】:

他想在不使用 Loop 的情况下实现它,而您的答案使用 Loop 来解决问题。【参考方案6】:

使用 Java 8,您可以在不使用(显式)循环的情况下声明和初始化二维数组,如下所示:

int x = 20; // first dimension
int y = 4; // second dimension

double[][] a = IntStream.range(0, x)
                        .mapToObj(i -> new double[y])
                        .toArray(i -> new double[x][]);

这将使用默认值初始化数组(double 的情况下为0.0)。

如果您想明确定义要使用的填充值,您可以添加DoubleStream

int x = 20; // first dimension
int y = 4; // second dimension
double v = 5.0; // fill value

double[][] a = IntStream
        .range(0, x)
        .mapToObj(i -> DoubleStream.generate(() -> v).limit(y).toArray())
        .toArray(i -> new double[x][]);

【讨论】:

这实现了目标,但并不简单。当流在版本 8 中出现时,Java 应该填补了这个空白。【参考方案7】:
public static Object[] fillArray(Object[] arr,Object item)
    Arrays.fill(arr, item);
    return arr;

Character[][] maze = new Character[10][10];
    fillArray(maze, fillArray(maze[0], '?'));

    for(int i = 0;i<10;i++)
        System.out.println();
        for(int j = 0;j<10;j++)
            System.out.print(maze[i][j]);
        
    

我希望它做得好

【讨论】:

这一点都不好!只需在fillArray(maze, fillArray(maze[0], '?')); 之后添加maze[2][2] = 'X';,您就会大吃一惊。问题是您只创建了一行的一个实例,并且您引用了它 10 次。您丢弃了原来创建的 10 行中的 9 行。 它无法赢得int[][] 数组。它适用于Character[][],因为Character 是一个对象。【参考方案8】:

难道我们都不希望有一个&lt;T&gt;void java.util.Arrays.deepFill(T[]…multiDimensional)。问题始于Object threeByThree[][] = new Object[3][3];threeByThree[1] = null;threeByThree[2][1] = new int[]42; 完全合法。 (如果只有 Object twoDim[]final[] 是合法且定义明确的……) (使用下面的公共方法之一可以保持调用源代码的循环。 如果您坚持根本不使用循环,请使用递归替换循环和对 Arrays.fill()(!) 的调用。)

/** Fills matrix @code m with @code value.
 * @return @code m's dimensionality.
 * @throws java.lang.ArrayStoreException if the component type
 *  of a subarray of non-zero length at the bottom level
 *  doesn't agree with @code value's type. */
public static <T>int deepFill(Object[] m, T value) 
    Class<?> components; 
    if (null == m ||
        null == (components = m.getClass().getComponentType()))
        return 0;
    int dim = 0;
    do
        dim++;
    while (null != (components = components.getComponentType()));
    filler((Object[][])m, value, dim);
    return dim;

/** Fills matrix @code m with @code value.
 * @throws java.lang.ArrayStoreException if the component type
 *  of a subarray of non-zero length at level @code dimensions
 *  doesn't agree with @code value's type. */
public static <T>void fill(Object[] m, T value, int dimensions) 
    if (null != m)
        filler(m, value, dimensions);


static <T>void filler(Object[] m, T value, int toGo) 
    if (--toGo <= 0)
        java.util.Arrays.fill(m, value);
    else
        for (Object[] subArray : (Object[][])m)
            if (null != subArray)
                filler(subArray, value, toGo);

【讨论】:

【参考方案9】:
Arrays.fill(arr, new double[4]);

【讨论】:

这一行使得每一行都指向同一个内存块,即改变 arr[1][5] 也会改变 arr[100][5]。【参考方案10】:

作为答案的扩展,我找到了这篇文章,但希望填充一个 4 维数组。 原始示例只是一个二维数组,但问题是“多维”。我不想为此发布新问题...

您可以使用相同的方法,但您必须将它们嵌套,以便最终获得一维数组。

fourDArray = new float[10][10][10][1];
// Fill each row with null
for (float[][][] row: fourDArray)

    for (float[][] innerRow: row)
    
        for (float[] innerInnerRow: innerRow)
        
        Arrays.fill(innerInnerRow, -1000);
        
    
;

【讨论】:

【参考方案11】:

这是因为double[][]double[] 的数组,您不能将0.0 分配给它(这就像double[] vector = 0.0)。事实上,Java 没有真正的多维数组。

碰巧,0.0 是 Java 中双精度数的默认值,因此当您从 new 获取矩阵时,矩阵实际上已经被零填充。但是,如果您想用 1.0 填充它,您可以执行以下操作:

我不相信 API 提供了一种无需使用循环即可解决此问题的方法。使用 for-each 循环很简单。

double[][] matrix = new double[20][4];

// Fill each row with 1.0
for (double[] row: matrix)
    Arrays.fill(row, 1.0);

【讨论】:

这也足够了Arrays.fill(arr, 0d);Arrays.fill(arr, (double)0); 我得到Exception in thread "main" java.lang.ArrayStoreException: java.lang.Double 除非我遍历行。 你为什么不试着理解这个答案呢?在 java(以及 C 和 C++)中没有多维数组!!!您的矩阵是一个简单的一维数组,其中每个“字段”又是一个维度数组。您对 Arrays.fill() 的调用试图将一个 int (双重写入 0.0 而不仅仅是 0)放入“矩阵”,这不起作用。 为了挑剔,Arrays.fill() 尝试在矩阵中的每个 row 中放置一个 double。【参考方案12】:
double[][] arr = new double[20][4];
Arrays.fill(arr[0], 0);
Arrays.fill(arr[1], 0);
Arrays.fill(arr[2], 0);
Arrays.fill(arr[3], 0);
Arrays.fill(arr[4], 0);
Arrays.fill(arr[5], 0);
Arrays.fill(arr[6], 0);
Arrays.fill(arr[7], 0);
Arrays.fill(arr[8], 0);
Arrays.fill(arr[9], 0);
Arrays.fill(arr[10], 0);
Arrays.fill(arr[11], 0);
Arrays.fill(arr[12], 0);
Arrays.fill(arr[13], 0);
Arrays.fill(arr[14], 0);
Arrays.fill(arr[15], 0);
Arrays.fill(arr[16], 0);
Arrays.fill(arr[17], 0);
Arrays.fill(arr[18], 0);
Arrays.fill(arr[19], 0);

【讨论】:

嗨。我只是想知道你为什么建议这个解决方案而不是经典的for。有动机吗?谢谢! 因为问题中有以下词语:“不使用循环”。我的解决方案显然很荒谬,但它确实正确地回答了这个问题。 对不起,错过了 OP 中的“不使用循环”:删除了我的反对票。也许你应该在你的回答中说明不应该太认真地对待这个建议。 不,这也是一个有趣的实验:正确答案可以获得多少反对票? @Bart:没问题-没关系:D【参考方案13】:

OP 询问如何解决这个问题没有循环!出于某种原因,如今避免循环很流行。为什么是这样?可能有人意识到使用mapreducefilter 和朋友,以及像each 这样的方法隐藏循环并减少程序冗长,并且有点酷。真正甜蜜的 Unix 管道也是如此。或 jQuery 代码。没有循环,事情看起来很棒。

但是Java 有map 方法吗?不是真的,但我们可以用Function 接口和evalexec 方法定义一个。这不是太难,将是一个很好的练习。它可能很昂贵并且没有在实践中使用。

另一种方法没有循环是使用尾递归。是的,这有点傻,也没有人会在实践中使用它,但它确实表明,也许,在这种情况下循环很好。尽管如此,只是为了展示“又一个无循环示例”并获得乐趣,这里是:

import java.util.Arrays;
public class FillExample 
    private static void fillRowsWithZeros(double[][] a, int rows, int cols) 
        if (rows >= 0) 
            double[] row = new double[cols];
            Arrays.fill(row, 0.0);
            a[rows] = row;
            fillRowsWithZeros(a, rows - 1, cols);
        
    

    public static void main(String[] args) 
        double[][] arr = new double[20][4];
        fillRowsWithZeros(arr, arr.length - 1, arr[0].length);
        System.out.println(Arrays.deepToString(arr));
    

这并不漂亮,但在回答 OP 的问题时,没有 explicit 循环。

【讨论】:

感谢您的所有解决方案!我不想使用循环的原因是什么?我一直在寻找一个简单的解决方案来生成一个 n×n 的零矩阵,就像在 MATLAB 中一样。 这可能仍然会导致堆栈溢出,因为Java没有尾递归支持,所以循环肯定>递归。 我会告诉你为什么它是“避免循环的时尚”。每个人和他的兄弟都幻想自己是一个 Linux 内核程序员。查看代码,您会发现循环很少且相差甚远;相反,人们拥有手动编码的测试和分支,并且代码中充满了 goto。这在 1974 年的早期 UNIX 内核中是有道理的,当时可以合理地认为编译器为循环生成了次优代码,但这在 2016 年几乎不适用。然而,每个失败者都认为这看起来非常高级,特别是如果他在代码与 inane cmets 并具有荷兰姓氏。【参考方案14】:

如何在不使用循环的情况下在 Java 中填充多维数组?

多维数组只是数组的数组,fill(...) 不会检查数组的类型和您传入的值(这个责任在开发人员身上)。

因此,如果不使用循环,您将无法很好地填充多维数组。

请注意,与 C 或 C++ 等语言不同,Java 数组是对象,并且在多维数组中,除了最后一级之外,所有数组都包含对其他 Array 对象的引用。我不能 100% 确定这一点,但它们很可能分布在内存中,因此你不能像 C/C++ 允许的那样只填充一个没有循环的连续块。

【讨论】:

以上是关于Arrays.fill 在 Java 中使用多维数组的主要内容,如果未能解决你的问题,请参考以下文章

JAVA源码走读二分查找与Arrays类(未完)

java.util.Arrays.fill方法

Java:Arrays类

Java基础_Arrays

为啥 Arrays.fill() 不再在 HashMap.clear() 中使用?

Java的操作数组的方法Arrays