4

以下のコードで何が起こっているのかを理解するのに問題があります。配列の振る舞いcdは私が期待するものです。しかし、何が起こっているのaでしょbうか?(これも通常のスカラー変数で試しましたが、どちらの場合も驚くべきことは何も起こりません。)

出力は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は値渡しです、くそっ!( Java配列の参照渡しも機能しませんか?を調べましたが、質問者の英語が理解できず、メソッド呼び出しによって例がわかりにくくなっています。)

d = tmp;行がコメントアウトされており、cおよびとd同じ奇妙な動作を示していることに注意してaくださいb。それでもどうしたらいいのかわからない。

誰もがどのようaにそしてbの振る舞いを値渡しで説明できるかを説明できますか?

編集:補遺

私の投稿の主な問題は、値渡しではなく、エイリアシングであることがわかりました。値渡しとポインターの違いを明確にするために、次のメソッドをコードに追加し、それを使用してスワップcと(上記のリンクされたJavaDudeの記事によってリンクされた記事dによって提案された)スワップを実行しました。

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

結果はそれでcあり、d変更されずに戻ってきます。これは、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;

ここで説明する相対的なタイミング。

4

4 に答える 4

11

ここでは「奇妙な」ことは何も起こっていません。配列変数は実際の配列への参照です(他の言語ではポインターとも呼ばれます)。配列変数を操作するときは、ポインターを操作するだけです。

配列変数を別の変数に割り当てるときは、割り当てた変数が指す配列のエイリアスを作成し、割り当てられた変数が以前に指していた配列をガベージコレクションの対象にします。割り当てa = baのエイリアスを作成するため、データのb入力bはデータの入力とまったく同じように機能aします。割り当てが完了するab、同じものの2つの異なる名前になります。

値の受け渡しに関する限り、この例では何も起こっていません。値の受け渡しの概念は、呼び出すメソッドにパラメーターとしてオブジェクトを渡す場合にのみ適用されます。あなたの例では、変数、、、はaメソッドパラメータではなく、ローカル変数です。メソッドへの参照によってそれらを渡します(または、より正確には、オブジェクトへの参照を値で渡します。Javaではすべてが値で渡されるためです)。そのため、によって行われた配列への変更は、戻り時に表示されます。メソッドから。bcdtoStringfilltoStringfillfill

于 2012-06-07T00:02:45.697 に答える
2

のような割り当てを行うと、およびがプリミティブでないa = b;場合、それらは同じオブジェクトを参照するようになります。あなたの場合、それらは同じ配列を参照するようになりました。どちらか一方に加えた変更は、もう一方にも影響します(1つだけを更新し、両方を更新しているため)。abab

この動作は、Javaでのパラメーターの受け渡し方法とは関係がないことに注意してください。

于 2012-06-07T00:04:52.270 に答える
1

配列初期化子は、内部でnewを呼び出しています(したがって、この場合は構文上のサッカリンです)。上部に近いスワップは、参照をスワップするだけです。下のスワップは、参照が実行すると予想されるとおりに実行されます。

リンクされた記事はパラメータを参照しています...Javaでは、すべてのパラメータは値によって渡されます。つまり、参照自体が値によって渡されるだけです。つまり、REFERENCE(逆参照されたコンテンツではない)への変更は、サブルーチンの範囲外には反映されません。

于 2012-06-07T00:00:55.667 に答える
0

配列には、Javaクラスで教えられたり見逃されたりしない重要なポイントがあります。配列が関数に渡されると、同じ配列に別のポインターが作成されます(同じポインターが渡されることはありません)。両方のポインターを使用して配列を操作できますが、呼び出されたメソッドで2番目のポインターを新しい配列に割り当て、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
    }
}
于 2017-02-16T05:44:53.397 に答える