Equated Java Arrays中的奇怪之处:引用与指针

时间:2012-06-06 23:57:25

标签: java arrays pointers

在了解以下代码的内容时遇到问题。数组cd的行为是我所期望的。但ab发生了什么? (我也尝试使用普通的标量变量,在任何一种情况下都不会发生任何意外。)

输出被复制到RH注释。

import java.util.Arrays;

public class ArraysParadox {

    public static void main(String[] args) {

        int[] c = {1, 2, 3};
        int[] d = {6, 5, 4, 3};

        System.out.print("c:       ");
        System.out.println(Arrays.toString(c)); // c:       [1, 2, 3]

        System.out.print("d:       ");
        System.out.println(Arrays.toString(d)); // d:       [6, 5, 4, 3]

        System.out.println("--- swap ---");
        int[] tmp = c;
        c = d;
        d = tmp;    // <----- Magic?

        System.out.print("c' (=d): ");
        System.out.println(Arrays.toString(c)); // c' (=d): [6, 5, 4, 3]

        System.out.print("d' (=c): ");
        System.out.println(Arrays.toString(d)); // d' (=c): [1, 2, 3]

        System.out.println("--- c = 0 ---");
        Arrays.fill(c, 0);
        System.out.print("c (=0):  ");
        System.out.println(Arrays.toString(c)); // c (=0):  [0, 0, 0, 0]

        System.out.print("d (=c):  ");
        System.out.println(Arrays.toString(d)); // d (=c):  [1, 2, 3]

        System.out.println("--- d = 1 ---");
        Arrays.fill(d, 1);
        System.out.print("c (=d):  ");
        System.out.println(Arrays.toString(c)); // c (=d):  [0, 0, 0, 0]

        System.out.print("d (=1):  ");
        System.out.println(Arrays.toString(d)); // d (=1):  [1, 1, 1]

        System.out.println("================");

        int[] a = {1, 2, 3};
        int[] b = {6, 5, 4, 3};

        System.out.print("a:       ");
        System.out.println(Arrays.toString(a)); // a:       [1, 2, 3]

        System.out.print("b:       ");
        System.out.println(Arrays.toString(b)); // b:       [6, 5, 4, 3]

        a = b;
        System.out.print("a (=b):  ");
        System.out.println(Arrays.toString(a)); // a (=b):  [6, 5, 4, 3]

        System.out.println("--- α = 0 ---");
        Arrays.fill(a, 0);
        System.out.print("a (=0):  ");
        System.out.println(Arrays.toString(a)); // a (=0):  [0, 0, 0, 0]
        System.out.print("b (=a?): ");
        System.out.println(Arrays.toString(b)); // b (=a?): [0, 0, 0, 0]    ???

        System.out.println("--- b = 1 ---");
        Arrays.fill(b, 1);
        System.out.print("b (=1):  ");
        System.out.println(Arrays.toString(b)); // b (=1):  [1, 1, 1, 1]
        System.out.print("a (=b?): ");
        System.out.println(Arrays.toString(a)); // a (=b?): [1, 1, 1, 1]
    }
}

cd的可交换性表示根据以下帖子传递值:Java is Pass-by-Value, Dammit!。 (我也查看java array pass by reference does not work?,但我无法理解提问者的英语,方法调用模糊了这个例子。)

请注意,在d = tmp;行被注释掉后,cd表现出与ab相同的奇怪行为。我仍然不知道该怎么做。

任何人都可以解释ab的行为如何通过传值来解释?

编辑:附录

事实证明,我的帖子中的主要问题不是传值,而是别名。为了清楚传值和指针之间的区别,我在代码中添加了以下方法并用它来(尝试)交换cd(由{{3建议)通过上面链接的JavaDude文章链接。

static <T> void swap (T c, T d) {
    T tmp = c;
    c = d;
    d = tmp;
}

结果是cd恢复原状。如果Java(如C)将指向cd的指针传递给方法,那么这将起作用,但它只是传递它们的值,保持原始变量不变。

a = b更改为a = b.clone();a = Arrays.copyOf(b, b.length);会产生我期望的行为。此代码也有效:

    int[] tmp = new int[b.length];
    System.arraycopy( b, 0, tmp, 0, b.length );
    a = tmp;

相对时间描述article

4 个答案:

答案 0 :(得分:11)

这里没有什么“怪异”:数组变量是引用到实际数组(在其他语言中也称为指针)。当你操纵数组变量时,你所做的就是操纵指针。

当您将数组变量分配给另一个变量时,可以为您指定的变量指向的数组创建别名,并使之前指向的变量指向的数组符合垃圾条件采集。由于作业a = b使a成为b的别名,因此使用数据填充b与使用数据填充a完全相同:一旦作业完成,ab只是两个不同的名称。

传递值而言,在您的示例中没有进行任何操作:仅当您将对象作为参数传递给您调用的方法时,传递值的概念才适用。在您的示例中,变量abcd不是方法参数,它们是局部变量。您确实通过引用方法toStringfill来传递它们(或者更确切地说,您将对象的引用传递给toStringfill,因为在Java中一切是按值传递的,这就是为什么在从方法返回时可以看到由fill完成对数组的修改。

答案 1 :(得分:2)

当您进行a = b;之类的作业时,如果ab不是基元,那么它们现在引用相同的对象。在您的情况下,他们现在引用相同的数组。您对其中任何一项所做的任何更改也会影响另一项(因为您只更新了一件事,ab都指向它。)

请注意,此行为与参数在Java中的传递方式无关。

答案 2 :(得分:1)

数组初始化程序在引擎盖下调用new(所以在这种情况下它是语法糖精)。您在顶部附近的交换只是交换引用,下面的交换正好执行您期望引用执行的方式。

链接的文章引用参数...在Java中,所有参数都是按值的,只是引用本身通过值传递,即对REFERENCE(而不是其解除引用的内容)的更改不会反映在范围之外。子程序。

答案 3 :(得分:0)

在java类中通常没有教授或遗漏数组的重要一点。当数组传递给函数时,会为同一个数组创建另一个指针(永远不会传递相同的指针)。您可以使用两个指针来操作数组,但是一旦将第二个指针分配给被调用方法中的新数组并通过void返回调用函数,则原始指针仍保持不变。

您可以在此处直接运行代码:https://www.compilejava.net/

import java.util.Arrays;

public class HelloWorld
{
    public static void main(String[] args)
    {
        int Main_Array[] = {20,19,18,4,16,15,14,4,12,11,9};
        Demo1.Demo1(Main_Array);
        // THE POINTER Main_Array IS NOT PASSED TO Demo1
        // A DIFFERENT POINTER TO THE SAME LOCATION OF Main_Array IS PASSED TO Demo1

        System.out.println("Main_Array = "+Arrays.toString(Main_Array));
        // outputs : Main_Array = [20, 19, 18, 4, 16, 15, 14, 4, 12, 11, 9]
        // Since Main_Array points to the original location,
        // I cannot access the results of Demo1 , Demo2 when they are void.
        // I can use array clone method in Demo1 to get the required result,
        // but it would be faster if Demo1 returned the result to main
    }
}

public class Demo1
{
    public static void Demo1(int A[])
    {
        int B[] = new int[A.length];
        System.out.println("B = "+Arrays.toString(B)); // output : B = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        Demo2.Demo2(A,B);
        System.out.println("B = "+Arrays.toString(B)); // output : B = [9999, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        System.out.println("A = "+Arrays.toString(A)); // output : A = [20, 19, 18, 4, 16, 15, 14, 4, 12, 11, 9]

        A = B;
        // A was pointing to location of Main_Array, now it points to location of B
        // Main_Array pointer still keeps pointing to the original location in void main

        System.out.println("A = "+Arrays.toString(A)); // output : A = [9999, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        // Hence to access this result from main, I have to return it to main
    }
}
public class Demo2
{
    public static void Demo2(int AAA[],int BBB[])
    {
        BBB[0] = 9999;
        // BBB points to the same location as B in Demo1, so whatever I do
        // with BBB, I am manipulating the location. Since B points to the
        // same location, I can access the results from B
    }
}