3

Beginner Java question: when I have

Integer i = 6;
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);

then I write i = 8, ar.get(0) returns 6.

But if I trie the same thing with a class of mine:

class MyC
{
    Integer i;
}
MyC myc = new MyC();
myc.i = 6;
ArrayList<MyC> ar = ArrayList<MyC>();
ar.add(myc);

then do myc.i = 8, ar.get(0) returns 8.

Can you please explain this behavior?

hovnatan
  • 1,331
  • 10
  • 23

6 Answers6

3

The problem has nothing to do with autoboxing, as some answers say.

In the first example you create an Integer and put it in the ArrayList. Then you change the pointer to the Integer, so that i is pointing to another Integer. This don't affects the Integer of the ArrayList.

In the second example you create an object and put it in the ArrayList. Then you change the state of this object by myc.i = 8. This way the object in the ArrayList is changed.

F. Böller
  • 4,194
  • 2
  • 20
  • 32
2

This is because i = 8 is turned by the compiler into i = new Integer(8), since i is of Integer type, not int, so you have 2 references now. In your second example, there's still one reference referenced from both myc and first element of ar.

As pointed out by Sotirios in comment, if i was primitive, you'll get the same result. But because of slightly different reason:

int i = 6;
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);
i = 8; // changes not a ref, but value on stack
System.out.println(ar.get(0)); // prints out 6

Here you have not two references, but one (to autoboxed Integer inside ar).

Victor Sorokin
  • 11,878
  • 2
  • 35
  • 51
2

All variables, with the exception of primitive types, store references, not values.

First example

Integer i = 6;

Create a new Integer object (lets call it I1) and store a reference to it in i

ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);`

Create an ArrayList, and store a reference to I1 in it

i = 8;

Create a new (different) Integer object (lets call it I2) and store a reference to it in i

So now, i = I2, and ar.get(0) = I1

Second example

MyC myc = new MyC();

Create a new MyC (let's call it C) and store a reference to in in myc

myc.i = 6;

Create a new Integer object (lets call it I1) and store a reference to it in C.i

ArrayList<MyC> ar = ArrayList<MyC>();
ar.add(myc);

Create an ArrayList, and store a reference to C in it.

myc.i = 8

Create a new Integer object (lets call it I2) and store a reference to it in C.i

So now myc = C, myc.i = I2, ar.get(0) = C, and therefore ar.get(0).i = I2.

Nothing is referencing I1 and it will be garbage collected.

Jamie Cockburn
  • 7,379
  • 1
  • 24
  • 37
1

Integer is an Object-wrapper around the primitive type of int.

In order to store them in a Collection for example List<Integer> list = new ArrayList<Integer>(), the stored element type needs to be a subclass of Object. Therefore, they are stored by reference, as all Objects are stored as references (and all methods receive references by value, see Parameter passing in Java )

It is important to note that in the case of

List<Integer> list = new ArrayList<Integer>();
list.add(5);
int number = list.get(0);
System.out.println("" + number);

The reason why 5 can be used to add the number is because of auto-boxing. When you get the number from the list, it also implicitly calls .intValue() and returns the value of the wrapper as a primitive int. Then in the println() function, the number is implicitly boxed to Integer and then toString() is called.

Community
  • 1
  • 1
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
0

All variables in Java are references that refer to objects that live out on the heap.

The wrapper classes (e.g. Integer) are special cases. Those implement the Flyweight pattern and are immutable.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 1
    I don't think it's a problem of autoboxing. In the first example the pointer to i is changed after inserting, in the second example the object myc itself is changed. – F. Böller Jul 04 '14 at 14:48
0

Pointing i to a different object does not mutate the (original) object. This is not special to primitives. Say you have a List<List<Object>> or a List<List<String>>:

        List<List<String>> wrapper = new ArrayList<List<String>>();

        List<String> l1 = new ArrayList<String>();

        wrapper.add(l1);

        l1 = new ArrayList<String>();
        l1.add("hello");
        for(List<String> list : wrapper)
        {
            for(String string : list)
            {
                System.out.println(string);
            }
        }

If you now iterate over wrapper you find it contains 1 empty list. This is equivalent to your first example. Your second example looks like:

        List<List<String>> wrapper = new ArrayList<List<String>>();

        List<String> l1 = new ArrayList<String>();

        wrapper.add(l1);

        l1.add("hello");
        for(List<String> list : wrapper)
        {
            for(String string : list)
            {
                System.out.println(string);
            }
        }

In this case the wrapper now contains 1 list with one value in it.

Brett Okken
  • 6,210
  • 1
  • 19
  • 25
  • Right, because you're storing the original ArrayList in the wrapper, and then you just modify the reference of the variable. But that's not actually the original ArrayList that was stored in the wrapper. – EpicPandaForce Jul 04 '14 at 14:45