如何实现Java ArrayList按引用传递?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (619)
public class test
{
    public static void main(String[] args)
    {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        modifyList(list);
        for(int a : list)
        {
            System.out.println(a); // prints "1" 
        }

        list = new ArrayList<>();
        for(int a : list)
        {   
            System.out.println(a); // prints nothing
        }
    }

    public static void modifyList(List<Integer> list)
    {
        list = new ArrayList<>();
    }
}

有没有人知道为什么在调用modifyList()之后,list仍然包含元素1而不是指向一个什么都不包含的新地址?我的理解是arrayList是一个对象,因此它可以通过引用传递,它是可变的。例如,如果我在modifyList()中执行list.add(2),则同一列表中将包含2个元素。

提问于
用户回答回答于

new ArrayList实例化了一个集合对象,在某个地方浮动在内存中。返回给你的list不是真正的新ArrayList对象。返回的是在哪里找到浮动的对象的地址

想象一下,引用变量是一个小孩,该变量中的值(地址)是孩子手中的一个字符串,而该ArrayList字符串另一端是一个氦气球。孩子不是气球,但孩子可以带你气球。

这与C语言中的指针相同,只是Java从不让程序员看到地址值。每当您访问list 引用变量时,JVM都会自动跟随它包含的地址(“在C语言中取消引用指针”)以在内存中找到该对象。

传递list变量时,会生成该地址的副本并将其发送到被调用的方法。现在,被调用的方法可以找到ArrayList浮动的同一个对象。当我们进入被调用的方法时,有一个ArrayList对象,但是有两个引用变量(指针,真的):原始list和参数变量恰好被命名为相同,list。这两个变量共享相同名称的事实是无关紧要的,没有任何影响,也没有意义。

但是当你实例化第二个时ArrayList,你丢弃了复制的地址,获得了该新对象的地址,并将第二个地址分配给该方法中的变量。您将传递的参数值(引用地址)替换为不同的值,将不同的引用地址替换为不同的ArrayList对象。此时我们有两个引用变量和两个ArrayList对象。

当您返回到原始调用方法时,原始list变量仍保留原始参考地址ArrayList。从调用方法的角度来看,没有任何改变。

当你离开被调用的方法时,它的参数变量保持对第二个新创建的引用ArrayList超出范围,消失成为垃圾收集的候选者。同上第二个新创建的ArrayList对象。此时,我们有一个原始未更改的引用变量和一个原始未更改的ArrayList对象仍保留其原始分配的项目。

顺便说一下,将传递的参数变量更改为指向调用方法最初预期的其他对象是不好的做法。您丢失了重要信息,原始信息。没有任何好处,但是为了节省一些将由另一个引用变量使用的字节。更改传递的参数变量的值可能会导致代码和错误混淆。要防止重新分配,请使用标记每个参数的声明final。包括我在内的一些人认为这应该是Java中的默认行为,因为它是一种设计良好的语言。

提示:当传递集合时,请考虑可变性。您可能希望传递集合的副本或集合的不可修改版本。这避免了被调用的方法弄乱了调用方法背后的集合结构。请参阅Collections实用程序类和新List.of…(和Map.of…)方法。

用户回答回答于

参数of modifyList是原始引用的副本,这意味着它指向同一个对象,但它不是相同的引用

现在list = new ArrayList<>();,复制引用指向一个新对象,但原始引用仍指向原始列表。

扫码关注云+社区

领取腾讯云代金券