Java: 创建一个数组的副本而不使其成为引用

Java: Create a duplicate of an array without making it a reference

我写了一系列矩阵运算,其中我采用二维浮点数组,将其视为矩阵,并对其执行矩阵运算以获得逆矩阵。我的问题是,虽然我使用 class 方法的数组不是 class 的一部分,但每次我 运行 使用数组作为参数的方法时,数组本身也会被修改。

首先我将描述如何得到我的矩阵的逆,然后我将展示输出。

矩阵求逆的步骤如下:

  1. 获取辅因子矩阵(即创建原始矩阵的矩阵次要矩阵,然后取反所有其他条目。如果C = Cofactor Matrix,M = Matrix of Minors,i是当前行,j是当前列,然后 C[ i ][ j ] = M[ i ][ j ]*( -1 )^( i + j )
  2. 通过转置(用其类似的列、行条目替换行、列条目,反之亦然)辅助因子矩阵,将辅助因子矩阵转换为辅助(也称为伴随)矩阵。如果 C = Cofactor Matrix,A = Adjugate Matrix,i 是当前行,j 是当前列,则 A[ i ][ j ] = C[ j ][ i ]
  3. 最后,对原始矩阵取一个以上的行列式,并将辅助矩阵乘以该值。如果 I = 逆矩阵,A = 调整矩阵和 D = 行列式,则 I = (1/D)*A
  4. 为了检验你是否真正掌握了矩阵的逆矩阵,可以用原矩阵乘以它的逆矩阵得到单位矩阵。 如果 I = 逆矩阵,O = 原始矩阵,id = 单位矩阵,则 O*I = id

现在我将介绍实现这些操作的代码。为了简洁起见,我不会描述如何获得Minor矩阵或行列式,但我遇到的问题无论如何都会变得明显。

public class MatrixOperations {
    //Note: this method works fine. There are no problems.
    public float determinant(float [][] a)
    {
        float [][] temp_mat;
        float res = 0;
        //assuming a square matrix
        /*If it's a 2X2, then use the formula for a determinant of
        2X2 matrices.*/
        if(a.length == 2)
        {
            return a[0][0]*a[1][1]-a[0][1]*a[1][0];
        }
        /*Otherwise do the determinant formula recursively until your
        determinant is made up of 2X2 matrix determinants and scalar products*/
        else
        {
            temp_mat = new float[a.length-1][a.length-1];
            int placej = 0;
            int placei = 0;
            for(int k = 0; k<a.length;k++)
            {
                for(int j = 0; j<a.length; j++)
                {
                    for(int i = 1; i < a.length; i++)
                    {
                        placei = i-1;

                        if(j != k)
                        {
                            if(j < k)
                            {
                                temp_mat[placei][j] = a[i][j];
                            }
                            else if(j > k)
                            {
                                if (i == 1){
                                    placej = j-1;
                                }
                                temp_mat[placei][placej] = a[i][j];
                            }
                        }
                    }
                }

                res+=a[0][k]*determinant(temp_mat)*(int)Math.pow(-1, k);
            }
            return res;
        }
    }
    //Note: this method also works fine
    //Scalar product method
    public float[][] mul(float[][] m, float r)
    {
        float[][] res = new float[m.length][m.length];

        for(int i = 0; i < m.length; i++)
        {
            for(int j = 0; j < m.length; j++)
            {
                res[i][j]= m[i][j]*r;
            }
        }

        return res;

    }
    //Note: This method also works fine
    public float[][] mul(float[][] m,float[][] n)
    {
        float[][] res = new float[m.length][m.length];

        for(int i = 0; i < m.length; i++)
        {
            for(int j = 0; j < m.length; j++)
            {
                for(int k = 0; k < m.length; k++)
                {
                    res[i][j] += m[i][k]*m[k][i];
                }
            }
        }

        return res;

    }
    //The method for creating a matrix of minors
    //Here I start having problems
    public float[][] minor(float [][] m)
    {
        float [][] minor_mat = new float [m.length][m.length];
        //If the matrix is greater than a 2X2, use this to generate a matrix of minors
        if(m.length > 2)
        {
            float [][] current_minor = new float [m.length-1][m.length-1];
            int placei = 0;
            int placej = 0;
            for(int i = 0; i < m.length; i++)
            {
                for(int j = 0; j < m.length; j++)
                {
                    for(int k = 0; k < m.length; k++)
                    {
                        for(int l = 0; l < m.length; l++)
                        {
                            if(i != k && j != l)
                            {
                                if(k<i)
                                    placei = k;
                                else if(k>i)
                                    placei = k-1;
                                if(l<j)
                                    placej = l;
                                else if(l>j)
                                    placej = l-1;

                                current_minor[placei][placej] = m[k][l];
                            }
                        }
                    }
                    minor_mat[i][j] = this.determinant(current_minor);
                }
            }
        }
        //otherwise use the definition for 2X2 matrix of minors
        else
        {
            //even though minor_mat is using m.clone() rather than m, when I return the result, m has still been modified for some reason.
            minor_mat = m.clone()
            float temp;
            temp = minor_mat[0][0];
            minor_mat[0][0] = minor_mat[1][1];
            minor_mat[1][1] = temp;
            temp = minor_mat[0][1];
            minor_mat[0][1] = minor_mat[1][0];
            minor_mat[1][0] = temp;
        }
        return minor_mat;
    }
    //the same problem occurs here as it did in the minor method
    //m appears to get modified even though I only use m.clone()
    public float[][] cofactor(float [][] m)
    {
        float[][] res = m.clone();
        res = this.minor(res)
        for(int i = 0; i < m.length; i++)
        {
            for(int j = 0; j < m.length; j++)
            {
                res[i][j] = res[i][j]*(int)Math.pow(-1, i + j);
            }
        }
        return res;
    }

    //The following transpose, adjugate, and inverse methods have the same problem        

    public float[][] transpose(float[][] m)
    {
        float[][] res = new float[m.length][m.length];
        float temp = 0;
        for(int i = 0; i < m.length; i++)
        {
            for(int j = 0; j < m.length; j++)
            {
                temp = m[i][j];
                res[i][j] = m[j][i];
                res[j][i] = temp;       
            }
        }
        return res;
    }
    public float[][] adjugate(float[][] m)
    {
        float[][] res = this.transpose(this.cofactor(m));
        return res;
    }
    public float[][] inverse(float[][] m)
    {
        float[][] res = this.mul(this.adjugate(m), (1/this.determinant(m)));
        return res;
    }
    //print out the matrix in square form
    public void matrixprint(float [][] m)
    {
        for(int i = 0; i < m.length; i++)
        {
            System.out.println("");
            for(int j = 0; j < m[i].length; j++){
                System.out.print(m[i][j] + " ");
            }
        }
        System.out.println("\n");
    }
}

现在是主要 class 和创建 MatrixOperations class 实例并在 2X2 矩阵上使用其方法的主要方法。

public class Main {

    public static void main(String[] args) {
        MatrixOperations mo = new MatrixOperations();

        //Create a 2X2 matrix called "matrix" and set its elements
        //Then perform each step on "matrix" and finally test if you have acquired the correct inverse

        float [][] matrix = new float[2][2];
        matrix[0][0] = 2;
        matrix [0][1] = 5;
        matrix [1][0] = 4;
        matrix [1][1] = 3;

        System.out.println("Matrix = ");
        mo.matrixprint(matrix);
        System.out.println("Minor = ");
        mo.matrixprint(mo.minor(matrix));
        System.out.println("Matrix = ");
        mo.matrixprint(matrix);
        System.out.println("Cofactor = ");
        mo.matrixprint(mo.cofactor(matrix));
        System.out.println("Matrix = ");
        mo.matrixprint(matrix);
        System.out.println("Adjugate = ");
        mo.matrixprint(mo.adjugate(matrix));
        System.out.println("Matrix = ");
        mo.matrixprint(matrix);
        System.out.println("Determinant = ");
        System.out.println(mo.determinant(matrix));
        System.out.println("Matrix = ");
        mo.matrixprint(matrix);
        System.out.println("Inverse = ");
        mo.matrixprint(mo.inverse(matrix));
        System.out.println("Matrix = ");
        mo.matrixprint(matrix);
        System.out.println("Identity = ");
        mo.matrixprint(mo.mul(mo.inverse(matrix), matrix));

    }

}

现在你会看到,当我显示输出时,每次我在"matrix"上使用方法并重新打印"matrix",即使我的方法也被修改了"matrix"只使用 "matrix" 的副本而不是 "matrix" 本身。

输出:

Matrix = 

2.0 5.0 
4.0 3.0 

Minor = 

3.0 4.0 
5.0 2.0 

Matrix = 

3.0 4.0 
5.0 2.0 

Cofactor = 

3.0 -4.0 
-5.0 2.0 

Matrix = 

3.0 -4.0 
-5.0 2.0 

Adjugate = 

3.0 5.0 
4.0 2.0 

Matrix = 

3.0 4.0 
5.0 2.0 

Determinant = 
-14.0
Matrix = 

3.0 4.0 
5.0 2.0 

Inverse = 

-0.21428573 0.35714287 
0.2857143 -0.14285715 

Matrix = 

3.0 -4.0 
-5.0 2.0 

Identity = 

0.1479592 0.1479592 
0.12244898 0.12244898

任何 help/explanation 为什么会发生这种情况,我们将不胜感激。

这一行做一个浅层克隆;

float[][] res = m.clone();

这将复制 res,它是对数组的引用的数组。但不是 res 指向的任何数组。很可能你想要的是

float[][] res = new float[m.length][];
for (int i = 0; i < m.length; i++)
    res[i] = m[i].clone();

这是因为您在 MatrixOperations class 的方法中传递了 matrix 对象的引用。它不是 matrix 对象的副本。

来自Java doc

Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before.

二维数组就是数组的数组。 clone() 对数组进行浅表克隆。 所以你有一个新的克隆外部数组,但它引用相同的条目(内部数组)。 克隆外部数组后,遍历外部数组并克隆所有内部数组以获得深度克隆。