2
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();
}
}

As you can see, class C is an inner class of B. Within B, I have an array of objects of type C, aptly called "c". Within C, I have a constructor that accepts an array of integers.

What I'm doing here is for each element in the array of C's, I'm generating a random list of 5 integers and passing it to the constructor of the corresponding C Object.

This code works as expected; every object in the array of Cs has associated with it a different array of random integers. But if I remove the line "t = new int[5]" at the end of the first for-loop (ie., if I don't "reset" t), then every object in my array of Cs prints the same 5 numbers that were the last to be assigned.

In other words, if I change this bit

    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];
    }

to this

    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);
    }

the output changes from this

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

to this

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

Why does that happen? shouldn't each new C object get a new set of inputs since after every loop, the contents of t are changed, regardless of whether the line "t = new int[5]" is there or not?

Manuel
  • 2,143
  • 5
  • 20
  • 22
  • 1
    When asking for help, please take the time to format and indent your code readably and consistently. Making your post and code clear, and demonstrating that you took the time to do so, improves your chances of getting good answers. – T.J. Crowder Oct 06 '16 at 07:51
  • Btw.: if you understood what the problem is, then you should also be able to see that the line `int[] data = new int[5];` isn't very useful... – dingalapadum Oct 09 '16 at 12:07

4 Answers4

9

But if I remove the line "t = new int[5]" at the end of the first for-loop (ie., if I don't "reset" t), then every object in my array of Cs prints the same 5 numbers that were the last to be assigned.

That's because by not creating a new array, you're making all of the entries in c refer to the same array. So naturally you see the same contents of the array.

I'd suggest that since t is only useful in regard to a specific loop iteration, you declare and create it in the loop. Remember: Variables should be scoped as narrowly as possible. So:

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();
    }
}

To illustrate what's going on both with and without creating a new array, let's do some ASCII-art for what's in memory as we go, but with 3 elements rather than 5 to keep the pictures smaller:

With the line creating a new array each time:

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];
}

Right before the loop, we have this in memory:

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

Note those values in t and c, which I've shown there as Ref5462 and Ref2634 respectively. Those are object references. They're values (just like an int is a value), and they tell the Java runtime where the array they refer to is, elsewhere in memory. That is, the array isn't in the variable, the location of the array is in the variable. (We never see the actual values, the numbers I'm using here are just conceptual.)

Then we run our j loop and fill in values in t:

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

Then we store a copy of the value of t in c[0]:

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

Note how c[0] and t contain the same value now. They both refer to the same array. There is no link between c[0] and t, they just have the same value in them.

Then we create a new array and store the new reference to it in 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    |
                                                 +−−−−−−−−−+

Note how t has a new reference in it, which points to the new array. c[0] still points to the old one.

Now we loop again and fill in the new t, then store that new t's value in 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    |
                                                  +−−−−−−−−−+

Note how c[0] and c[1] refer to different arrays.

Then we do it all again, creating another array, and end up with this:

[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    |
                                                                 +−−−−−−−−−+

Now, let's look at it if you don't create a new t each time:

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];
}

At first, things seem the same. Here we are after the first loop again:

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

But at this point, we don't create a new array. So after the second loop, the previous array that t refers to has new values in it, and we've stored a copy of its location in c[1]:

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

Now, t, c[0], and c[1] all refer to the same array. After the next loop, we've updated the contents of that array again and pointed c[2] at it:

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

So naturally, when you output it, you see the same values repeated.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • But in the second 'for' loop, contents of t are being changed for each outer 'for' loop iteration. How the output is still the same? Can you please explain? – vv88 Oct 06 '16 at 07:58
  • @vv88: Because it's the same array, as I said. – T.J. Crowder Oct 06 '16 at 08:01
  • 1
    This was fantastic, thank you. I appreciate you taking the time to draw the ASCII illustrations, haha. – Manuel Oct 11 '16 at 03:59
  • Just wondering: how did you draw this ascii art? Handmade? – GhostCat Apr 26 '17 at 18:49
  • @GhostCat: Yup. Just me and vim. :-) – T.J. Crowder Apr 27 '17 at 06:36
  • Has vim "special" support for such drawings? Or are you telling me that you really really type the ---- chars and and boxes and arrows all manually? – GhostCat Apr 27 '17 at 06:38
  • @GhostCat: Manually. It's not hard with vi keybindings, column selection, etc. First I use `+` and `-`, then just before posting I do a search and replace to replace `-` with `−` ([U+2212](http://unicode.org/cldr/utility/character.jsp?a=2212)) so it lines up better with the `+`. I tried a couple of drawing tools and they were more of a pain than they were worth, and hard to modify later (http://yuml.me was close, though). I figure ASCII-art (Unicode-art, actually) is more in keeping with SO's markdown-instead-of-WYSIWYG approach. :-) – T.J. Crowder Apr 27 '17 at 07:06
2

I think you can see what you are doing wrong if you add System.out.println(java.util.Arrays.toString(t)); after you call c[i] = new C(t);. The problem is that you give the reference to the new instance of C as parameter. If you change the content of the array later, you also change the content of the "old" instances of C, because they use the same instance of t. You could copy the array if you don't want to make a new one: c[i] = new C(java.util.Arrays.copyOf(t, t.length)); Then you can remove the line t = new int[5];

Bambino
  • 405
  • 4
  • 15
1

EDIT: Java primitive arrays are NOT inmutable, I was wrong.

What happens is very simple. Each time you do

t = new int[5]

you are pointing t to a new reference. When you don't write this line, you are just replacing your t elements, but not its reference so all your C objects are pointing to the same t array. At the end of the loops you just have your array t with the last 5 random numbers and all your 5 C objects pointing to it. What you can do, if don't want to put that line, is to copy values inside your C constructor instead of just making data POINT to input. There are better ways, but you can just do this as a quick fix for understanding what hapens with the "=" operator:

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();
    }
}

now you'll have a new array inside each C, so it won't be affected when you assign new values to your t inside the loop.

Rubasace
  • 939
  • 8
  • 18
1

Since I think the other explanations are good, instead of trying to explain to you again, I will show you this is small example which should help you understand. You should be able to copy-paste it into your main and run it. Then try figuring it out:

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!
dingalapadum
  • 2,077
  • 2
  • 21
  • 31