简体   繁体   English

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

[英]Behaviour of an array when passed as a parameter in recursive calls

I thought I understood the concept of pass by value and reference in Java. 我以为我了解Java中按值传递和引用的概念。 But I just found out that I don't. 但是我只是发现我没有。

Below are two methods that are being recursively called. 下面是递归调用的两种方法。 In the first method, I am passing an array in the recursive calls, from top to bottom. 在第一种方法中,我从上到下在递归调用中传递一个数组。 When the condition is met, it prints the array and then returns. 满足条件时,将打印数组,然后返回。 As the recursive calls return, the elements added to the array are removed with the return from each level. 当递归调用返回时,添加到数组的元素将从每个级别的返回中删除。

But if I use an ArrayList , the additions of elements to the list is permanent, even after all the recursive calls return. 但是,如果我使用ArrayList ,则即使在所有递归调用返回之后,列表中元素的添加也是永久的。

I assumed that in both cases, I am passing the array and Arraylist by reference (copy of reference) and I'm directly modifying the array. 我假设在两种情况下,我都是通过引用(引用的副本)传递数组和Arraylist ,而我是直接修改数组。 So whatever changes i make in the array, shouldn't it remain after the method returns? 因此,无论我在数组中进行任何更改,方法返回后都应该保留吗? Please suggest, where am I getting it wrong. 请提出建议,我在哪里弄错了。

I have not added the print() , I guess its evident from the output. 我还没有添加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);
}

Output from First 第一的输出

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

Output from second 秒输出

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]

Java only uses pass by value. Java仅使用按值传递。 That means: If you pass an int (or any other primitive) to a method, the method works on a copy of that value. 这意味着:如果将int(或任何其他原语)传递给方法,则该方法将在该值的副本上工作。 The same applies to references: If you change the reference in the method, the caller doesn't care. 这同样适用于引用:如果您在方法中更改引用,则调用者将不在乎。

Now, in java int[] is a object, so, you have a reference which you pass to the method. 现在,在java中,int []是一个对象,因此,您具有传递给该方法的引用。 If the method changes the content of the array, the caller sees the change. 如果该方法更改了数组的内容,则调用者将看到更改。 If you assign a another array to the reference, the caller doesn't see the new array reference. 如果将另一个数组分配给引用,则调用者将看不到新的数组引用。

Nice article about java parameter passing 关于Java参数传递的不错的文章

Now to your problem: I think your print method uses a for loop in the array case, which then uses num as a boundary and in the array list case you use to string. 现在解决您的问题:我认为您的打印方法在数组的情况下使用for循环,然后使用num作为边界,在数组列表的情况下使用字符串。 But thats a guess, please post your print method(s?) 多数民众赞成在一个猜测,请发布您的打印方法。

Edit : 编辑

Just as I thought: You use toString() on the list, so the whole content will be printed. 就像我想的那样:您在列表上使用toString(),因此将打印所有内容。 Since you call that after the recursive call, the list is already populated. 由于您是在递归调用之后调用该列表的,因此该列表已被填充。 Since the array version of the method uses the index, you only print the content till this index. 由于该方法的数组版本使用索引,因此仅打印内容直到该索引。 eg if index is 1 you print element at 0 where the toString() of ArrayList prints everything. 例如,如果index为1,则在0处打印元素,其中ArrayList的toString()打印所有内容。

If you change your print method to use the length field instead of index you should see the same output as in the list version: 如果将打印方法更改为使用长度字段而不是索引,则应该看到与列表版本相同的输出:

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 is a object wrapper for int. 整数是整数的对象包装。 For your code, it doesn't make a semantic difference, but you allocate memory on the heap to store the actual int value. 对于您的代码,它没有语义上的区别,但是您在堆上分配了内存以存储实际的int值。 Use int, where possible. 尽可能使用int。 For the ArrayList generic (T) you cannot use int, since int is not part of the object type system (a little sloppy formulated). 对于ArrayList泛型(T),您不能使用int,因为int不是对象类型系统的一部分(有点草率地表述)。 You have to use Integer. 您必须使用Integer。

I think maybe your problem is that you're having trouble getting your head around recursion. 我认为也许您的问题是您难以理解递归。 Join the crowd -- everybody else has the same problem, which is one reason to avoid recursion (another being not to blow up the call stack). 加入人群-每个人都有相同的问题,这是避免递归的一个原因(另一个原因是不要使调用堆栈崩溃)。

Try setting a breakpoint on the first line of the recurse method (either one), then step through it with the debugger to follow the actuall execution path. 尝试在递归方法的第一行(任意一个)上设置一个断点,然后与调试器一起逐步执行它,以遵循实际的执行路径。

What you'll find is that the basic logic is set next number set next number set next number set next number set next number print print print print print 您会发现基本逻辑是设置下一个数字集下一个数字集下一个数字集下一个数字集下一个数字集下一个数字打印打印打印打印打印

If you remove the index from the int[] version of your print() method, you'll find that you have the same output as for the ArrayList vresion. 如果从print()方法的int []版本中删除索引,则会发现您的输出与ArrayList vresion的输出相同。

Here you are doing pass by reference only (both in the case of array and ArrayList). 在这里,您仅通过引用进行传递 (对于array和ArrayList而言)。 You are passing the reference of array from the main method which in turn is passed to every recursive call. 您正在从main方法传递数组的引用,而该方法又传递给每个递归调用。 So if you change anything in the array in each recursive call,then that change in the array will be reflected to called function. 因此,如果您在每次递归调用中更改了数组中的任何内容,那么数组中的更改将反映到被调用的函数中。 So you are correct about what you have learnt for pass by reference and pass by value. 因此,您对于通过引用传递和通过值所学的知识是正确的。

But your print method is confusing you and making things complicated for you. 但是您的打印方法使您感到困惑,并使您的工作变得复杂。 In your print method that takes ArrayList as an argument, you are printing the complete arraylist each after each recursive call. 在将ArrayList作为参数的打印方法中,您将在每次递归调用之后分别打印完整的arraylist。 But for you array you are only printing it till particular index. 但是对于数组,您仅将其打印到特定索引为止。 You need to print complete array as you did with arrayList. 您需要像使用arrayList一样打印完整的数组。 Therefore, you can change the function signature of print method as following 因此,您可以按以下方式更改打印方法的功能签名

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();       

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM