在递归调用中作为参数传递时数组的行为

时间:2014-07-26 17:19:23

标签: java arrays recursion pass-by-reference

我以为我理解了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]

3 个答案:

答案 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相同的输出。