2017-07-02 54 views
0

我遇到它简化到像这样的一个问题:为什么ArrayList副本的行为与整数副本不一样?

public static void main(String[] args) { 
    ArrayList<String> firstList = new ArrayList<>(); 
    firstList.add("Hello"); 
    firstList.add("World"); 
    firstList.add("This"); 
    firstList.add("Is"); 
    firstList.add("A"); 
    firstList.add("Test"); 

    System.out.println("Size of firstList: " + firstList.size()); 

    ArrayList<String> tempList = firstList; 
    System.out.println("Size of tempList: " + tempList.size()); 

    firstList.clear(); 
    System.out.println("Size of firstList: " + firstList.size()); // should be 0 
    System.out.println("Size of tempList: " + tempList.size()); 
} 

,输出是:

Size of firstList: 6 
Size of tempList: 6 
Size of firstList: 0 
Size of tempList: 0 

我期望的tempList第二次大小轮是6而不是0

已经有一些与此效果有关的问题,如this oneanother one

从答案,我发现,这是因为tempList是指同一基准firstList所以当firstList变化也是如此tempList(纠正我,如果我错了这里)。

因此,一个更好的解决方案,这将是这样的:

ArrayList<String> tempList = new ArrayList<String>(firstList); 

如果有关引用的上述信息是真实的,那么,为什么这样的代码:

public static void main(String[] args) { 
    int firstValue = 5; 

    System.out.println("firstValue: " + firstValue); 

    int tempValue = firstValue; 
    System.out.println("tempValue: " + firstValue); 

    firstValue = 3; 
    System.out.println("firstValue: " + firstValue); 
    System.out.println("tempValue: " + tempValue); 
} 

给这个输出:

firstValue: 5 
tempValue: 5 
firstValue: 3 
tempValue: 5 

应该tempValue不是也是3第二次打印?

我觉得我误解了引用是如何工作的,所以有人可以解释为什么第一个示例中的临时列表和原始列表一起受到影响,而临时整数和原始整数给出与第二个示例中不同的结果?

回答

0

由于原始值没有引用,并且包装类型是不可变的(因此Integer的行为方式相同)。数组作为反例,是引用类型(甚至是基元数组)。所以,

int[] arr = { 1 }; 
int[] arrCopy = Arrays.copyOf(arr, arr.length); // <-- deep copy 
int[] b = arr; // <-- shallow copy 
b[0] = 2; // <-- same as arr[0] = 2 
System.out.printf("arr = %s, arrCopy = %s, b = %s%n", // 
     Arrays.toString(arr), Arrays.toString(arrCopy), // 
     Arrays.toString(b)); 

输出

arr = [2], arrCopy = [1], b = [2] 

也参见,What is the difference between a deep copy and a shallow copy?

1

对于ArrayList的例子中,两个变量同一对象别处在堆存储器中,从而改变之一的内容参考影响另一个。

至于整数例子,当前的行为是预期的,因为它们是原始类型而不是引用类型。当涉及基本类型时,变量将存储该值本身,而不是对内存中对象的引用。

2

Java有两种变量类型:基元和引用。当一个变量分配给另一个,如在

ArrayList<String> tempList = firstList; 

要分配存储在firstList是可变tempList的值的参考。重要的是要注意,您并未使用此作业创建新的ArrayList对象;你只是简单地复制一个参考值。

当你与原语分配:

int tempValue = firstValue; 

你正在做同样的事情,但有一个原始值。

一般来说,可以这样想。变量不共享内存位置;每个变量都有自己的值。但是,如果该值是引用,则两个引用变量可能包含相同的引用(就像两个基本变量可能包含相同的基本值,例如5)。对于参考类型,如果您使用一个变量来更改所引用对象的内容,那么您将在通过第二个变量访问时看到该对象中的更改。在计算机科学文献中,这种行为被称为aliasing。对于原语,这种别名不会发生。

1

int tempValue是一个原始类型,这意味着它直接按值存储。 ArrayList tempList不是原始类型,因此通过引用进行存储。

您看到的与int发生的事情也会发生在所有其他Java静态类型的基本变量中。同时,Java非基元变量在变化时会随着变化引起的变化而变化。

(这会导致后续问题:?当您使用firstList = new ArrayList()代替firstList.clear()发生了什么tempList是它的价值重新分配和突变,为什么之间的相同)

相关问题