为什么我的数组中的最后一个元素会覆盖以前的所有元素?

时间:2016-10-06 07:48:19

标签: java arrays object

import java.util.Random;

public class B {
private class C {
    int[] data = new int[5];
    C (int[] input) {data = input;}

    void print() {
        for (int i=0; i<5; i++)
            System.out.print(data[i] + " " );
        System.out.println();
    }
}

C[] c;

B () {
    Random r = new Random();
    c = new C[5];
    int[] t = new int[5];

    for (int i=0; i<5; i++) {
        for (int j=0; j<5; j++)
            t[j] = r.nextInt(10) + 1;
        c[i] = new C(t);
        t = new int[5];
    }

    for (int k=0; k<5; k++)
        c[k].print();
}

public static void main(String[] args) {
    B b = new B();
}
}

如您所见,C类是B的内部类。在B中,我有一个C类型的对象数组,恰当地称为“c”。在C中,我有一个接受整数数组的构造函数。

我在这里做的是对于C数组中的每个元素,我正在生成一个包含5个整数的随机列表,并将其传递给相应C对象的构造函数。

此代码按预期工作; Cs数组中的每个对象都有一个不同的随机整数数组。但是如果我在第一个for循环的末尾删除行“t = new int [5]”(即,如果我没有“重置”t),那么我的Cs数组中的每个对象都会打印出来5个最后被分配的数字。

换句话说,如果我改变这个位

    for (int i=0; i<5; i++) {
        for (int j=0; j<5; j++)
            t[j] = r.nextInt(10) + 1;
        c[i] = new C(t);
        t = new int[5];
    }

到这个

    for (int i=0; i<5; i++) {
        for (int j=0; j<5; j++)
            t[j] = r.nextInt(10) + 1;
        c[i] = new C(t);
    }

输出从此

更改
9 6 5 3 7
2 7 7 3 9
4 5 8 3 9
9 8 3 5 8
4 8 5 5 4

到这个

7 1 5 8 9
7 1 5 8 9
7 1 5 8 9
7 1 5 8 9
7 1 5 8 9

为什么会这样?不应该每个新的C对象都得到一组新的输入,因为在每次循环之后,t的内容都会改变,无论是否存在“t = new int [5]”行?

4 个答案:

答案 0 :(得分:9)

  

但是如果我在第一个for循环结束时删除“t = new int [5]”行(即,如果我没有“重置”t),那么我的Cs数组中的每个对象打印最后一个要分配的5个数字。

这是因为如果不创建新数组,那么c中的所有条目都会引用相同的数组。所以你自然会看到数组的相同内容。

我建议,因为t仅对特定循环迭代有用,所以在循环中声明并创建它。请记住:变量的范围应尽可能窄。所以:

B () {
    Random r = new Random();
    c = new C[5];
    // Don't declare or initialize it here: int[] t;  = new int[5];

    for (int i=0; i<5; i++) {
        int t[] = new int[5];           // *** Keep it specific to the loop, and
                                        //     create a new one each iteration
        for (int j=0; j<5; j++) {
            t[j] = r.nextInt(10) + 1;
        }
        c[i] = new C(t);
    }

    for (int k=0; k<5; k++) {
        c[k].print();
    }
}

为了说明在创建新数组和不创建新数组时发生了什么,让我们为内存中的内容做一些ASCII艺术,但是使用3个元素而不是5来保持图片更小:

每行创建一个新数组的行:

Random r = new Random();
c = new int[3];
int[] t = new int[3];
for (int i = 0; i < c.length; ++i) {
    for (int j = 0; j < t.length; ++j0 {
        t[j] = r.nextInt(10) + 1;
    }
    c[i] = t;
    t = new int[3];
}

在循环之前,我们在内存中有这个:

[t:Ref5462]−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+  |
[c:Ref2534]−−>| (array)    |  |
              +−−−−−−−−−−−−+  \   +−−−−−−−−−+
              | 0: null    |   +−>| (array) |
              | 1: null    |      +−−−−−−−−−+
              | 2: null    |      | 0: 0    |
              +−−−−−−−−−−−−+      | 1: 0    |
                                  | 2: 0    |
                                  +−−−−−−−−−+

请注意tc中的这些值,我将其分别显示为Ref5462Ref2634。这些是对象引用。它们是值(就像int是一个值),它们告诉Java运行时它们引用的数组在内存中别处。也就是说,数组不是 in 变量,数组的 location 在变量中。 (我们从未看到实际值,我在这里使用的数字只是概念性的。)

然后我们运行j循环并在t中填写值:

[t:Ref5462]−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+  |
[c:Ref2534]−−>| (array)    |  |
              +−−−−−−−−−−−−+  \   +−−−−−−−−−+
              | 0: null    |   +−>| (array) |
              | 1: null    |      +−−−−−−−−−+
              | 2: null    |      | 0: 9    |
              +−−−−−−−−−−−−+      | 1: 6    |
                                  | 2: 5    |
                                  +−−−−−−−−−+

然后我们会在t中存储c[0]的值的副本:

[t:Ref5462]−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+  |
[c:Ref2534]−−>| (array)    |  |
              +−−−−−−−−−−−−+  \   +−−−−−−−−−+
              | 0: Ref5462 |−−−+−>| (array) |
              | 1: null    |      +−−−−−−−−−+
              | 2: null    |      | 0: 9    |
              +−−−−−−−−−−−−+      | 1: 6    |
                                  | 2: 5    |
                                  +−−−−−−−−−+

注意c[0]t现在包含相同的值。它们都引用相同的数组。 c[0]t之间没有关联,它们只有相同的值。

然后我们创建一个 new 数组,并在t中存储对它的新引用:

[t:Ref8465]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+                  |
[c:Ref2534]−−>| (array)    |                  |
              +−−−−−−−−−−−−+      +−−−−−−−−−+ |
              | 0: Ref5462 |−−−−−>| (array) | |
              | 1: null    |      +−−−−−−−−−+ |
              | 2: null    |      | 0: 9    | |
              +−−−−−−−−−−−−+      | 1: 6    | |
                                  | 2: 5    | |
                                  +−−−−−−−−−+ |  +−−−−−−−−−+
                                              +−>| (array) |
                                                 +−−−−−−−−−+
                                                 | 0: 0    |
                                                 | 1: 0    |
                                                 | 2: 0    |
                                                 +−−−−−−−−−+

请注意t中有一个新引用,它指向新数组。 c[0]仍然指向旧的。

现在我们再次循环并填写 new t,然后将新t的值存储在c[1]中:

[t:Ref8465]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+                  |
[c:Ref2534]−−>| (array)    |                  |
              +−−−−−−−−−−−−+      +−−−−−−−−−+ |
              | 0: Ref5462 |−−−−−>| (array) | |
              | 1: Ref8465 |−−−+  +−−−−−−−−−+ |
              | 2: null    |   |  | 0: 9    | |
              +−−−−−−−−−−−−+   |  | 1: 6    | |
                               |  | 2: 5    | |
                               |  +−−−−−−−−−+ \   +−−−−−−−−−+
                               +−−−−−−−−−−−−−−−+−>| (array) |
                                                  +−−−−−−−−−+
                                                  | 0: 2    |
                                                  | 1: 7    |
                                                  | 2: 7    |
                                                  +−−−−−−−−−+

请注意c[0]c[1]如何引用不同的数组。

然后我们再次完成所有操作,创建另一个数组,并以此结束:

[t:Ref3526]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+                                 |
[c:Ref2534]−−>| (array)    |                                 |
              +−−−−−−−−−−−−+      +−−−−−−−−−+                |
              | 0: Ref5462 |−−−−−>| (array) |                |
              | 1: Ref8465 |−−−+  +−−−−−−−−−+                |
              | 2: Ref3526 |−+ |  | 0: 9    |                |
              +−−−−−−−−−−−−+ | |  | 1: 6    |                |
                             | |  | 2: 5    |                |
                             | |  +−−−−−−−−−+    +−−−−−−−−−+ |
                             | +−−−−−−−−−−−−−−−−>| (array) | |
                             |                   +−−−−−−−−−+ |
                             |                   | 0: 2    | |
                             |                   | 1: 7    | |
                             |                   | 2: 7    | |
                             |                   +−−−−−−−−−+ \   +−−−−−−−−−+
                             +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−>| (array) |
                                                                 +−−−−−−−−−+
                                                                 | 0: 4    |
                                                                 | 1: 5    |
                                                                 | 2: 8    |
                                                                 +−−−−−−−−−+

现在,如果您每次都创建一个新的t,请查看它:

Random r = new Random();
c = new int[3];
int[] t = new int[3];
for (int i = 0; i < c.length; ++i) {
    for (int j = 0; j < t.length; ++j0 {
        t[j] = r.nextInt(10) + 1;
    }
    c[i] = t;
    // What if we leave this out? t = new int[3];
}

起初,事情似乎是一样的。在这里,我们再次进行第一次循环:

[t:Ref5462]−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+  |
[c:Ref2534]−−>| (array)    |  |
              +−−−−−−−−−−−−+  \   +−−−−−−−−−+
              | 0: Ref5462 |−−−+−>| (array) |
              | 1: null    |      +−−−−−−−−−+
              | 2: null    |      | 0: 9    |
              +−−−−−−−−−−−−+      | 1: 6    |
                                  | 2: 5    |
                                  +−−−−−−−−−+

但是在这一点上,我们不会创建一个新数组。因此,在第二个循环之后,t引用的前一个数组中包含新值,并且我们已在c[1]中存储了其位置的副本:

[t:Ref5462]−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+  |
[c:Ref2534]−−>| (array)    |  |
              +−−−−−−−−−−−−+  \   +−−−−−−−−−+
              | 0: Ref5462 |−−−+−>| (array) |
              | 1: Ref5462 |−−/   +−−−−−−−−−+
              | 2: null    |      | 0: 2    |
              +−−−−−−−−−−−−+      | 1: 7    |
                                  | 2: 7    |
                                  +−−−−−−−−−+

现在,tc[0]c[1]都是指相同的数组。在下一个循环之后,我们再次更新了该数组的内容并将c[2]指向它:

[t:Ref5462]−−−−−−−−−−−−−−−−−−−+
              +−−−−−−−−−−−−+  |
[c:Ref2534]−−>| (array)    |  |
              +−−−−−−−−−−−−+  \   +−−−−−−−−−+
              | 0: Ref5462 |−−−+−>| (array) |
              | 1: Ref5462 |−−/   +−−−−−−−−−+
              | 2: Ref5462 |−/    | 0: 7    |
              +−−−−−−−−−−−−+      | 1: 1    |
                                  | 2: 5    |
                                  +−−−−−−−−−+

很自然地,当你输出它时,你会看到重复的相同值。

答案 1 :(得分:2)

如果您在致电System.out.println(java.util.Arrays.toString(t));后添加c[i] = new C(t);,我认为您可以看到自己做错了什么。问题是您将C的新实例的引用作为参数。如果您稍后更改了数组的内容,还会更改&#34; old&#34;的内容。 C的实例,因为它们使用相同的t实例。如果您不想制作新数组,可以复制数组:c[i] = new C(java.util.Arrays.copyOf(t, t.length));然后您可以删除第t = new int[5];

答案 2 :(得分:1)

编辑:Java原始数组不可更改,我错了。

发生的事情非常简单。每次你做

t = new int[5]

你指向一个新的参考。当你不写这一行时,你只是替换你的t元素,而不是它的引用,所以你的所有C对象都指向同一个t数组。在循环结束时,您只需使用最后5个随机数的数组t以及指向它的所有5个C对象。如果不想放置该行,您可以做的是复制C构造函数中的值,而不是仅将数据POINT输入。有更好的方法,但你可以这样做,作为一个快速解决方案,以了解&#34; =&#34;操作者:

private class C {
    int[] data;;

    C(int[] input) {
        data = new int[data.length];
        for(int i = 0; i < data.length; i++){
            data[i] = input[i];
        }
    }

    void print() {
        for (int i = 0; i < data.length; i++)
            System.out.print(data[i] + " ");
        System.out.println();
    }
}

现在您在每个C中都有一个新数组,因此当您在循环内为t分配新值时,它不会受到影响。

答案 3 :(得分:1)

由于我认为其他解释是好的,而不是试图再向你解释,我会告诉你这是一个小例子,应该有助于你理解。您应该能够将其复制粘贴到main并运行它。然后尝试搞清楚:

Random r = new Random();

int[] t = new int[5];
for (int j=0; j<5; j++)
    t[j] = r.nextInt(10) + 1;

C c = new C(t);
c.print(); // 7 3 8 10 6 //ok everything clear so far

// now let's change the content of t - not touching c!
for (int j=0; j<5; j++)
    t[j] = r.nextInt(10) + 1;

c.print(); // 8 9 2 4 5 //huh? content changed... hmm

C c2 = new C(t);
c2.print(); // 8 9 2 4 5  //right...the same thing

for (int j=0; j<5; j++)
    t[j] = r.nextInt(10) + 1;

c.print();  // 10 8 8 4 7 // yeah, kind of expecting this
c2.print(); // 10 8 8 4 7 // but I still don't get why exactly

//ok let's make a new t
t = new int[5];
for (int j=0; j<5; j++)
    t[j] = r.nextInt(10) + 1;

c.print(); // 10 8 8 4 7  // oh!
c2.print(); // 10 8 8 4 7  //now changing t doesn't influence the C's anymore

C c3 = new C(t);
c3.print(); // 3 10 7 9 4 // ah, here is the new t..

for (int j=0; j<5; j++)
    t[j] = r.nextInt(10) + 1;

c.print(); // 10 8 8 4 7 // still the old stuff
c2.print(); // 10 8 8 4 7 // yeah, also still the old stuff
c3.print(); // 9 1 1 1 5 // aha! right!

//let's check if I understood it:

t = new int[5];
for (int j=0; j<5; j++)
    t[j] = r.nextInt(10) + 1;

c3.print(); // 9 1 1 1 5 // ok, I think I got it!