英文:
List<int[]> passed by reference?
问题
I was tweaking with the code a bit and found out that when I try to update the List<int[]>
I'm able to do it like below but not able to do it when my list is of the type List<Integer>
List<int[]> res = List.of(new int[]{1, 2}, new int[]{3, 4});
int[] tempList = res.get(0);
tempList[0] = 100;
System.out.println(Arrays.toString(res.get(0)));
List<Integer> list = List.of(1, 2, 3, 4);
Integer x = list.get(2);
x = 200;
System.out.println(list);
Is the int[] passed by reference? or how does this work internally?
代码中的List<int[]>
可以像下面这样更新,但是对于List<Integer>
类型的列表,无法像这样更新:
List<int[]> res = List.of(new int[]{1, 2}, new int[]{3, 4});
int[] tempList = res.get(0);
tempList[0] = 100;
System.out.println(Arrays.toString(res.get(0)));
List<Integer> list = List.of(1, 2, 3, 4);
Integer x = list.get(2);
x = 200;
System.out.println(list);
这里的int[]
是通过引用传递的吗?或者内部是如何工作的?
英文:
I was tweaking with the code a bit and found out that when I try to update the List<int[]>
I'm able to do it like below but not able to do it when my list is of the type List<Integer>
List<int[]> res = List.of(new int[]{1, 2},new int[]{3,4});
int[] tempList = res.get(0);
tempList[0] = 100;
System.out.println(Arrays.toString(res.get(0)));
List<Integer> list = List.of(1,2,3,4);
Integer x = list.get(2);
x = 200;
System.out.println(list);
<img src="https://i.stack.imgur.com/vGTP0.png">
Is the int[] passed by reference? or how does this work internally?
答案1
得分: 3
在值传递和引用传递方面,请参阅 https://stackoverflow.com/q/40480/20228153。
在你的情况下,int[]
情况下,你没有修改 int[]
的值。相反,int[]
的值是对整数数组的引用,你正在使用该引用来修改数组,因此原始数组也被修改。
List<int[]> res = List.of(new int[]{1, 2}, new int[]{3, 4});
当执行上述操作时,你初始化了两个 int[]
对象,分别称为数组1和数组2,然后将数组1和数组2的引用放入了List<int[]> res
中。
int[] tempList = res.get(0);
当你这样做时,你获取了数组1的引用,并将其放入变量 tempList
中。
因此,始终只有一个数组1的副本,因此当你通过存储在 tempList
中的引用来修改它时,数组1被修改了,因此当你稍后尝试使用 res.get(0)
检索它时,你检索到了已修改的 int[]
。
List<Integer> list = List.of(1, 2, 3, 4);
这是类似的。你创建了4个 Integer
对象,并将它们的引用存储在 List<Integer> list
中。
Integer x = list.get(2);
你获取了第三个 Integer
对象的引用,并将其放入变量 x
中。
x = 200;
这就是使它与之前不同的地方。你直接改变了变量 x
,从之前创建的对象的引用到包含200的新创建的 Integer
对象的引用。但是,这只改变了变量 x
,并不改变存储在你的 list
中的引用。
这就是为什么在稍后检查时 list
没有发生变化的原因。
英文:
For pass-by-value vs. pass-by-reference, see https://stackoverflow.com/q/40480/20228153.
In your case, the int[]
case, you are not modifying the value of int[]
. Instead, the value of int[]
is a reference to an int array, and you are using that reference to modify the array, thus the original array is also modified.
List<int[]> res = List.of(new int[]{1, 2},new int[]{3,4});
When doing above, you initialized two int[]
objects, let's call them array 1 and array 2 respectively, and you put the references of array 1 and array 2 inside a List<int[]> res
.
int[] tempList = res.get(0);
When you do this, you get reference of array 1, and put it in variable tempList
.
Therefore, there is always only 1 copy of array 1, so when you modify it through its reference you stored in tempList
, that array 1 is modified, so when you try to retrieve it later using res.get(0)
, you retrieved the modified int[]
.
List<Integer> list = List.of(1,2,3,4);
This is similar. You created 4 Integer
objects, and stored their references in List<Integer> list
.
Integer x = list.get(2);
You get the reference of the third Integer
object and put it in variable x
.
x = 200;
This is what makes it different. You are directly changing variable x
, from the reference of your previously created object to the reference of a newly created object of Integer
that contains 200. However, this only changes variable x
, but does not change the reference stored in your list
.
That is why list
ends up not changing when you check later.
答案2
得分: 0
所有 Java 对象都是通过引用传递的。如果你熟悉 C 语言,通常被称为 指针,尽管在 Java 设计时该术语被认为有点受损(受损是指人们可能认为 '指针' 意味着你可以执行 '我将它加 2' 或类似的操作),所以 Java 把它们称为 '引用'。但是,本质是相同的。
而数组也是对象。除了硬编码的基本数据类型列表(int、long、double、float、boolean、short、byte、char)外,所有东西都是对象。
所以,给定:
List<int[]> foo = new ArrayList<int[]>();
int[] x = {1, 2, 3};
foo.add(x);
发生了以下事情:
- 创建一个新的 ArrayList。它没有名称。通过在堆上写入一些字节来创建它。
- 将堆上的 位置(所以不是列表本身,而是指向它的指针)存储在变量
foo
中。foo
当前指向它。将来,foo
可能指向其他列表;其他变量也可能稍后指向这个相同的 ArrayList。 - 创建一个新的 int 数组。它没有名称。通过在堆上写入一些字节来创建它。
- 将堆上的 位置 存储在变量
x
中。 - 调用列表的
add
方法。传递指针的副本(因为在 Java 中,一切都是按副本传递的,但请注意,一切都是引用。Java 永远不会悄悄地复制大型数据结构,只复制指针)。add 方法执行其操作。
实际上,它将该指针存储在其列表结构中。
一些更相关的见解:
按副本传递
int x = 10;
String y = "Hello"; // 字符串在 Java 中是对象。y 是一个指针。
test(x, y);
System.out.println(x);
System.out.println(y);
public void test(int x, String y) {
x = 20;
y = "World";
}
上述代码打印 10, Hello - Java 是按副本传递的。test 方法更改了它的值的副本是无关紧要的。test 方法更改了它自己的变量(String y
),开始时包含了指向 Hello
对象的指针的副本,并将其更改为指向其他对象,这也是无关紧要的:它们是副本;这不会影响调用者。
int[] x = [1, 2];
x[0] = 5;
x[0]
是 Java 语言的写法:取 x
指针。对它进行解引用(跟随指针到达的数组对象)。找到第一个元素并用新值替换它。因此,当 x
为 null 时,x[0]
会引发 NullPointerException(NPE 的意思是:你试图对 null 进行解引用)。鉴于 x[0]
跟随指针:
int[] x = {1, 2, 3};
test(x);
System.out.println(Arrays.toString(x));
public void test(int[] z) {
z[0] = 8;
}
会打印 8,2,3。该方法有其自己的指向 int 数组的指针的副本,但 test
方法跟随该指针并对其找到的对象执行某些操作。z 是一个副本,但它指向相同的东西。这就像如果我有一个包含房子地址的地址簿。如果我复制了我的地址簿页面并将复制件递给你,那么:
- 如果你将你的复制品撕成碎片,我不在乎。我甚至不会知道你这样做了。
- 如果你决定按照上面的地址并朝窗户扔一块砖头,哦,我关心这个。
第一个片段相当于撕毁了我的地址簿副本。第二个片段相当于走到上面的地址并扔砖。
不可变对象
一旦你解引用了一个指针,你可以做的事情取决于该指针末尾的是什么类型的对象。例如,如果它是一个数组,我可以更改它的一个值,而你无法阻止我这样做(Java 没有 '只读数组' 或类似的东西)。如果它是,比如,一个 AtomicInteger
,我可以在它上面调用 .set(10)
并更改它代表的值。
但是,如果该对象的类没有可以更改其任何状态的可访问方法,那么它就像是一个防弹玻璃房子。它对扔砖头是免疫的。你可以整天分发指向它的指针副本,而无需担心你分发的代码会扔砖头。因为窗户对它没有影响。这就是所谓的 '不可变对象'。
String 和 Integer 都是这样的。这是行不通的:
Integer y = new Integer(20);
y.setValue(40);
没有 set 方法。根本没有可以改变任何东西的方法。查看一下 javadoc。没有一个可以改变任何东西的方法。String - 也是一样的。toLowerCase()
方法不会 '更改这个字符串的内容'。它 通过将该字符串小写化创建一个全新的字符串;它不会更改这个字符串。
int[]
英文:
All java objects are passed by reference. If you're familiar with C, that's usually called a pointer, although the term was considered somewhat tainted at the time java was designed (tainted in the sense one might think 'pointer' implies you can do things like 'I shall add 2 to it' or some such), so java calls them 'reference'. But, same thing.
And arrays are objects. Everything is, except the hardcoded list of primitives (int, long, double, float, boolean, short, byte, char).
So, given:
List<int[]> foo = new ArrayList<int[]>();
int[] x = {1, 2, 3};
foo.add(x);
This is happening:
- Create a new arraylist. It has no name. Create it by writing some bytes on the heap.
- Store the position in the heap (so not the list itself, the pointer to it) in variable
foo
.foo
currently points at it. It may point at other lists in the future; other variables may point at this same arraylist later, too. - Create a new int array. It has no name. Create it by writing some bytes on the heap.
- Store the position in the heap in variable
x
. - Call the list's
add
method. Pass a copy of the pointer (because in java, everything is pass-by-copy, but note that everything is a reference. Java never copies giant datastructures silently, just pointers). The add method does whatever it does.
Effectively, of course, it stores that pointer in its list structure.
A few more relevant insights:
Pass-by-copy
int x = 10;
String y = "Hello"; // strings are objects in java. y is a pointer.
test(x, y);
System.out.println(x);
System.out.println(y);
public void test(int x, String y) {
x = 20;
y = "World";
}
The above code prints 10, Hello - java is pass by copy. The fact that test changes its copy of this value is immaterial. The fact that the test changes its own variable (String y
) that started out containing a copy of the pointer to the Hello
object and changed it to point at some other object, is immaterial: They are copies; it doesn't affect the caller.
int[] x = [1, 2];
x[0] = 5;
x[0]
is java-ese for: Take the x
pointer. Dereference it (follow the pointer to the array object you find there). Find the first element and replace it with a new value. Hence, x[0]
when x
is null causes NullPointerException (NPE means: You tried to dereference null). Given that x[0]
follows the pointer:
int[] x = {1, 2, 3};
test(x);
System.out.println(Arrays.toString(x));
public void test(int[] z) {
z[0] = 8;
}
Would print 8,2,3. The method has its own copy of the pointer to the int array, but the test
method follows this pointer and does something to the object it finds there. z is a copy but it points to the same thing. It's like if I have an address book with the address of a house on it. If I copy my page from my address book and hand you the copy, then:
- If you tear you copy to pieces, I don't care. I won't even be able to tell you did this.
- If you decide to follow the address on it and toss a brick through the window, oh, I care about that.
The first snippet was the equivalent of ripping up the copy of my address book page. This second snippet is the equivalent of walking to the address on it and tossing bricks.
Immutables
What you can do once you dereference a pointer depends on what kind of object is at the end of that pointer. For example, if it is an array, I can change a value of it, and you can't stop me from doing this (java does not have 'read only arrays' or some such). If it is, say, an AtomicInteger
, I can call .set(10)
on it and change the value it represents.
However, if the object's class has no accessible methods that change any of its state, then it's like a house that is bullet proof. It's impervious to brick throwing. You can hand copies of a pointer out all day without having to fear code you handed a copy of the address to throws a brick through the window. Cuz the windows are impervious to it. This is called 'an immutable object'.
String and Integer both are like this. This doesn't work:
Integer y = new Integer(20);
y.setValue(40);
There is no set method. At all. Check the javadoc. Not a single method that changes anything. String - same thing. The toLowerCase()
method does not 'change this strings contents'. It makes an entirely new string by lower casing this string; it does not change this string.
int[]
is not immutable. Integer
is.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论