java集合存储值或引用?

时间:2014-07-04 14:33:33

标签: java collections reference

初学Java问题:我什么时候

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

然后我写i = 8ar.get(0)返回6

但如果我和我的一类人一样:

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

然后执行myc.i = 8ar.get(0)返回8

你能解释一下这种行为吗?

6 个答案:

答案 0 :(得分:2)

问题与自动装箱无关,正如一些答案所说的那样。

在第一个示例中,您创建一个Integer并将其放在ArrayList中。 然后将指针更改为Integer,以便i指向另一个Integer。 这不会影响ArrayList的整数。

在第二个示例中,您将创建一个对象并将其放在ArrayList中。 然后,您通过myc.i = 8更改此对象的状态。 这样就可以更改ArrayList中的对象。

答案 1 :(得分:2)

除基本类型外,所有变量都存储引用,而不是值。

第一个例子

Integer i = 6;

创建一个新的Integer对象(我们称之为 I1 )并在i

中存储对它的引用
ArrayList<Integer> ar = ArrayList<Integer>();
ar.add(i);`

创建ArrayList,并在其中存储对 I1 的引用

i = 8;

创建一个新的(不同的)Integer对象(我们称之为 I2 )并在i

中存储对它的引用

现在,i = I2 ar.get(0) = I1

第二个例子

MyC myc = new MyC();

创建一个新的MyC(让我们称之为 C )并在myc

中存储对in的引用
myc.i = 6;

创建一个新的Integer对象(我们称之为 I1 )并在 C 中存储对它的引用。i

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

创建ArrayList,并在其中存储对 C 的引用。

myc.i = 8

创建一个新的Integer对象(我们称之为 I2 )并在 C 中存储对它的引用。i

所以现在myc = C myc.i = I2 ar.get(0) = C ,因此ar.get(0).i = I2

没有任何内容引用 I1 ,它将被垃圾收集。

答案 2 :(得分:1)

这是因为编译器将i = 8转换为i = new Integer(8),因为i属于Integer类型,而不是int,因此您有2个引用现在。在第二个示例中,仍有一个参考引用mycar的第一个元素。

正如Sotirios在评论中所指出的,如果i是原始的,你将得到相同的结果。但由于原因略有不同:

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

此处您没有两个引用,只有一个(在Integer内自动装箱ar)。

答案 3 :(得分:1)

Integer是基本类型int的对象包装器。

为了将它们存储在Collection中,例如List<Integer> list = new ArrayList<Integer>(),存储的元素类型必须是Object的子类。因此,它们通过引用存储,因为所有对象都存储为引用(并且所有方法都按值接收引用,请参阅Parameter passing in Java

重要的是要注意

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

5可用于添加数字的原因是因为自动装箱。从列表中获取数字时,它还隐式调用.intValue()并将包装器的值作为基本int返回。然后在println()函数中,将数字隐式地装入Integer,然后调用toString()。

答案 4 :(得分:0)

Java中的所有变量都是引用堆上存在的对象的引用。

包装类(例如Integer)是特殊情况。那些实现Flyweight模式并且是不可变的。

答案 5 :(得分:0)

i指向另一个对象不会改变(原始)对象。这对于原语来说并不特殊。假设您有List<List<Object>>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);
            }
        }

如果你现在迭代wrapper,你会发现它包含1个空列表。这相当于你的第一个例子。你的第二个例子如下:

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

在这种情况下,wrapper现在包含1个列表,其中包含一个值。