【问题标题】:Why does the last element in my array overwrite all previous elements? [duplicate]为什么我的数组中的最后一个元素会覆盖所有先前的元素? [复制]
【发布时间】:2017-02-14 20:51:54
【问题描述】:
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]”这一行是否存在?

【问题讨论】:

  • 在寻求帮助时,请花时间以可读且一致的方式格式化和缩进您的代码。使您的帖子和代码清晰,并证明您花时间这样做,可以提高您获得好的答案的机会。
  • 顺便说一句:如果您了解问题所在,那么您也应该能够看到int[] data = new int[5]; 这行不是很有用...

标签: java arrays object


【解决方案1】:

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

这是因为通过不创建新数组,您使c 中的所有条目都引用了 same 数组。所以很自然你会看到数组的相同内容。

我建议,由于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]−−>| (数组) | | +−−−−−−−−−−−−−+ \ +−−−−−−−−−−−+ | 0:空| +−>| (数组) | | 1:空 | +−−−−−−−−−−−+ | 2:空| | 0:0 | +−−−−−−−−−−−−−+ | 1:0 | | 2:0 | +−−−−−−−−−−−+

注意tc 中的那些值,我在此处分别显示为Ref5462Ref2634。这些是对象引用。它们是值(就像int 是一个值一样),它们告诉Java 运行时它们所引用的数组在内存中的其他地方。也就是说,数组不是in 变量,数组的位置 是在变量中。 (我们从来没有看到实际值,我在这里使用的数字只是概念性的。)

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

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

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

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

注意 c[0]t 现在如何包含相同的值。它们都引用同一个数组。 c[0]t 之间没有联系,它们只是具有相同的值。

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

[t:Ref8465]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | [c:Ref2534]−−>| (数组) | | +−−−−−−−−−−−−−+ +−−−−−−−−−−+ | | 0: Ref5462 |−−−−−->| (数组) | | | 1:空 | +−−−−−−−−−−+ | | 2:空| | 0:9 | | +−−−−−−−−−−−−−+ | 1:6 | | | 2:5 | | +−−−−−−−−−−+ | +−−−−−−−−−−−+ +−>| (数组) | +−−−−−−−−−−−+ | 0:0 | | 1:0 | | 2:0 | +−−−−−−−−−−−+

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

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

[t:Ref8465]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | [c:Ref2534]−−>| (数组) | | +−−−−−−−−−−−−−+ +−−−−−−−−−−+ | | 0: Ref5462 |−−−−−->| (数组) | | | 1: Ref8465 |−−−+ +−−−−−−−−−−+ | | 2:空| | | 0:9 | | +−−−−−−−−−−−−−+ | | 1:6 | | | | 2:5 | | | +−−−−−−−−−−+ \ +−−−−−−−−−−+ +−−−−−−−−−−−−−−−−+−>| (数组) | +−−−−−−−−−−−+ | 0:2 | | 1:7 | | 2:7 | +−−−−−−−−−−−+

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

然后我们再做一遍,创建另一个数组,最后得到这个:

[t:Ref3526]−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− −−−−−+ +−−−−−−−−−−−−−+ | [c:Ref2534]−−>| (数组) | | +−−−−−−−−−−−−−+ +−−−−−−−−−−+ | | 0: Ref5462 |−−−−−->| (数组) | | | 1: Ref8465 |−−−+ +−−−−−−−−−−+ | | 2: Ref3526 |−+ | | 0:9 | | +−−−−−−−−−−−−−+ | | | 1:6 | | | | | 2:5 | | | | +−−−−−−−−−−+ +−−−−−−−−−−+ | | +−−−−−−−−−−−−−−−−−−>| (数组) | | | +−−−−−−−−−−+ | | | 0:2 | | | | 1:7 | | | | 2:7 | | | +−−−−−−−−−−+ \ +−−−−−−−−−−+ +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−>| (数组) | +−−−−−−−−−−−+ | 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]−−>| (数组) | | +−−−−−−−−−−−−−+ \ +−−−−−−−−−−−+ | 0: Ref5462 |−−−+−>| (数组) | | 1:空 | +−−−−−−−−−−−+ | 2:空| | 0:9 | +−−−−−−−−−−−−−+ | 1:6 | | 2:5 | +−−−−−−−−−−−+

但此时,我们不创建新数组。所以在第二个循环之后,t 引用的前一个数组中有新值,我们在c[1] 中存储了它的位置副本:

[t:Ref5462]−−−−−−−−−−−−−−−−−−−−+ +−−−−−−−−−−−−−+ | [c:Ref2534]−−>| (数组) | | +−−−−−−−−−−−−−+ \ +−−−−−−−−−−−+ | 0: Ref5462 |−−−+−>| (数组) | | 1: Ref5462 |−−/ +−−−−−−−−−−+ | 2:空| | 0:2 | +−−−−−−−−−−−−−+ | 1:7 | | 2:7 | +−−−−−−−−−−−+

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

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

所以很自然,当您输出它时,您会看到重复的相同值。

【讨论】:

  • 但是在第二个 'for' 循环中, t 的内容会随着每个外部 'for' 循环迭代而改变。怎么输出还是一样?你能解释一下吗?
  • @vv88:因为它是同一个数组,就像我说的那样。
  • 这太棒了,谢谢。感谢您花时间绘制 ASCII 插图,哈哈。
  • 只是想知道:你是如何绘制这个 ascii 艺术的?手工制作?
  • @GhostCat:是的。只有我和vim。 :-)
【解决方案2】:

我想如果你在调用c[i] = new C(t); 之后添加System.out.println(java.util.Arrays.toString(t));,你会发现你做错了什么。问题是您将引用 C 的新实例作为参数。如果稍后更改数组的内容,也会更改 C 的“旧”实例的内容,因为它们使用相同的 t 实例。如果您不想创建一个新数组,可以复制该数组:c[i] = new C(java.util.Arrays.copyOf(t, t.length)); 然后您可以删除该行 t = new int[5];

【讨论】:

    【解决方案3】:

    编辑:Java 原始数组不是不可变的,我错了。

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

    t = new int[5]
    

    您将 t 指向一个新的引用。当你不写这一行时,你只是替换了你的 t 元素,而不是它的引用,所以你所有的 C 对象都指向同一个 t 数组。在循环结束时,您只需将数组 t 与最后 5 个随机数和所有 5 个 C 对象指向它。如果不想放置该行,您可以做的是在您的 C 构造函数中复制值,而不是仅仅使数据 POINT 输入。有更好的方法,但您可以将其作为快速解决方法来了解“=”运算符的情况:

    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 分配新值时它不会受到影响。

    【讨论】:

      【解决方案4】:

      由于我认为其他解释都很好,所以我不再试图向您解释,我将向您展示这个小例子,应该可以帮助您理解。您应该能够将其复制粘贴到您的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!
      

      【讨论】:

      • 谢谢,这也很有帮助。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-16
      • 2019-01-08
      • 1970-01-01
      • 2013-01-31
      相关资源
      最近更新 更多