我以为我理解了Java中传递值和引用的概念。但我发现我不知道。
以下是递归调用的两种方法。在第一种方法中,我在递归调用中从上到下传递一个数组。满足条件时,它会打印数组然后返回。随着递归调用的返回,添加到数组的元素将从每个级别返回。
但是如果我使用ArrayList
,那么即使在所有递归调用返回之后,列表中元素的添加也是永久性的。
我认为在这两种情况下,我都通过引用传递数组和Arraylist
(引用副本),并且我直接修改了数组。所以无论我在数组中做出什么改变,在方法返回后都不应该保留它吗?请提出建议,我在哪里弄错了。
我没有添加print()
,我猜它从输出中显而易见。
static void recurse(int[] list, int num) {
if(num == 5) { // print if condition is true
print("Reached num = 5", list, num);
return;
}
list[num] = num;
recurse(list,num+1);
print("After recursive call returns ", list, num);// check elements in array
}
static void recurse(ArrayList<Integer> list, int num) {
if(num == 5) { // print if condition is true
print("Reached num = 5", list);
return;
}
list.add(num);
recurse(list,num+1);
print("After recursive call returns ", list); // check element of Arraylist
}
private static void print(String msg, ArrayList<Integer> list) {
System.out.println(msg);
System.out.println(list);
}
private static void print(String msg, int[] list, int index) {
System.out.println(msg);
for(int i = 0 ; i < index; ++i)
System.out.printf("%d ", list[i]);
System.out.println();
}
public static void main(String[] args) {
ArrayList<Integer> arr1 = new ArrayList<>();
recurse(arr1,0);
int[] arr2 = new int[12];
recurse(arr2,0);
}
第一次
的输出Reached num = 5
0 1 2 3 4
After recursive call returns
0 1 2 3
After recursive call returns
0 1 2
After recursive call returns
0 1
After recursive call returns
0
After recursive call returns
第二次输出
Reached num = 5
[0, 1, 2, 3, 4]
After recursive call returns
[0, 1, 2, 3, 4]
After recursive call returns
[0, 1, 2, 3, 4]
After recursive call returns
[0, 1, 2, 3, 4]
After recursive call returns
[0, 1, 2, 3, 4]
After recursive call returns
[0, 1, 2, 3, 4]
答案 0 :(得分:1)
Java仅使用pass by value。这意味着:如果将int(或任何其他原语)传递给方法,则该方法将对该值的副本进行处理。这同样适用于参考:如果您更改方法中的参考,则来电者并不在意。
现在,在java中,int []是一个对象,因此,您有一个传递给该方法的引用。如果方法更改了数组的内容,则调用者会看到更改。如果为引用分配另一个数组,则调用者不会看到新的数组引用。
Nice article about java parameter passing
现在问题:我认为你的print方法在数组的情况下使用for循环,然后使用num作为边界,并在数组列表中使用字符串。但这是一个猜测,请发布您的打印方法(s?)
修改强>:
就像我想的那样:你在列表中使用toString(),因此将打印整个内容。由于您在递归调用后调用它,因此列表已填充。 由于方法的数组版本使用索引,因此只打印内容直到此索引。例如如果index为1,则将元素打印在0,其中ArrayList的toString()打印所有内容。
如果更改打印方法以使用长度字段而不是索引,则应该看到与列表版本相同的输出:
private static void print(String msg, int[] list, int index) {
System.out.println(msg);
for(int i = 0 ; i < list.length; ++i) {
System.out.printf("%d ", list[i]);
}
System.out.println();
}
Integer是int的对象包装器。对于您的代码,它不会产生语义差异,但您在堆上分配内存以存储实际的int值。尽可能使用int。对于ArrayList泛型(T),你不能使用int,因为int不是对象类型系统的一部分(有点草率制定)。你必须使用整数。
答案 1 :(得分:0)
这里你正在做仅通过引用传递(在数组和ArrayList的情况下)。您正在从main方法传递数组的引用,而main方法又传递给每个递归调用。因此,如果在每次递归调用中更改数组中的任何内容,则数组中的更改将反映到调用函数。所以你对通过引用传递和通过值传递所学到的知识是正确的。
但是你的打印方法让你感到困惑,让事情变得复杂。 在将ArrayList作为参数的print方法中,每次递归调用后都会打印完整的arraylist。 但对于你的阵列,你只是打印它直到特定的索引。您需要像使用arrayList一样打印完整数组。 因此,您可以将打印方法的功能签名更改为以下
private static void print(String msg, int[] list ) {
System.out.println(msg);
for(int i = 0 ; i <5; ++i)
System.out.printf("%d ", list[i]);
System.out.println();
}
答案 2 :(得分:-1)
我想也许你的问题是你无法理解递归。加入人群 - 其他人都有同样的问题,这是避免递归的另一个原因(另一个原因是不要炸掉调用堆栈)。
尝试在递归方法的第一行设置断点(任一个),然后使用调试器逐步执行它以跟随执行执行路径。
你会发现基本逻辑是什么 设置下一个号码 设置下一个号码 设置下一个号码 设置下一个号码 设置下一个号码 打印 打印 打印 打印 打印
如果从print()方法的int []版本中删除索引,则会发现您具有与ArrayList vresion相同的输出。