【问题标题】:Behaviour of an array when passed as a parameter in recursive calls在递归调用中作为参数传递时数组的行为
【发布时间】:2014-09-18 08:40:25
【问题描述】:

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

【问题讨论】:

  • 数组行为很好。这就是 Java 中按值传递的含义。看起来问题出在您的 print 方法中,因为您没有打印列表的全部内容,所以您得到了这个奇怪的结果。
  • 问题出在您的print 方法中。顺便说一句,您发布的输出与您发布的代码没有任何关系。
  • 您将num 传递给print 方法,所以这就是打印的数量。 num 未被递归调用更新。
  • Java 中没有“通过引用传递”的概念。
  • 添加了您的打印方法的更改版本

标签: java arrays recursion pass-by-reference


【解决方案1】:

Java 仅使用按值传递。这意味着:如果您将一个 int(或任何其他原语)传递给一个方法,该方法将对该值的副本起作用。这同样适用于引用:如果您更改方法中的引用,调用者不会在意。

现在,在 java 中 int[] 是一个对象,因此,您有一个传递给该方法的引用。如果方法更改了数组的内容,调用者会看到更改。如果您将另一个数组分配给该引用,则调用者不会看到新的数组引用。

Nice article about java parameter passing

现在解决您的问题:我认为您的 print 方法在数组情况下使用 for 循环,然后使用 num 作为边界,在数组列表情况下使用字符串。但这是一个猜测,请发布您的打印方法?

编辑

就像我想的那样:你在列表上使用 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 不是对象类型系统的一部分(有点草率)。您必须使用整数。

【讨论】:

  • 问题不在于数组执行,而在于ArrayList递归调用执行。
  • 我已经添加了 print() 方法。
  • 我还有一个问题。如果我使用 Integer[] 会有什么行为
  • 你在列表中使用 toString,所以整个内容都会被打印出来 System.out.println 内部会调用List#toString。问题是阵列的打印方式。 OP 应该改用Arrays#toString(arr)
  • Integer 是 int 的对象包装器。对于您的代码,它不会产生语义差异,但是您在堆上分配内存来存储实际的 int 值。尽可能使用 int 您不能将泛型与原始类型一起使用,ArrayList&lt;int&gt; 不可编译。
【解决方案2】:

我认为您的问题可能是您无法理解递归。加入人群 - 其他人都有同样的问题,这是避免递归的一个原因(另一个是不要炸毁调用堆栈)。

尝试在递归方法的第一行(任何一个)设置断点,然后使用调试器单步执行它以遵循实际的执行路径。

你会发现基本逻辑是 设置下一个号码 设置下一个号码 设置下一个号码 设置下一个号码 设置下一个号码 打印 打印 打印 打印 打印

如果您从 print() 方法的 int[] 版本中删除索引,您会发现您的输出与 ArrayList vresion 的输出相同。

【讨论】:

  • '其他人'根本没有同样的问题。不理解某事可能是避免它的一个理由,但它更有可能被说成是努力并学会理解它的一个理由。
  • 而 OP 的问题并不是真正的递归。
  • 嗯..从这几天开始,我确实沉迷于编写递归代码,但这个问题与它无关。在另一个代码中,我不加思索地用 arraylist 替换了一个数组,我得到了错误的输出。我没有调试我的代码,而是以错误的方式打印它。感谢您强调使用调试器的重要性。
【解决方案3】:

这里你正在仅通过引用传递(在数组和 ArrayList 的情况下)。您正在从 main 方法传递数组的引用,该方法又被传递给每个递归调用。因此,如果您在每次递归调用中更改数组中的任何内容,那么数组中的更改将反映到被调用的函数。所以你对引用传递和价值传递所学的知识是正确的。

但是您的打印方法让您感到困惑并使事情变得复杂。 在将 ArrayList 作为参数的 print 方法中,每次递归调用后,您都将打印完整的 arraylist。 但是对于你的数组,你只打印它直到特定的索引。您需要像使用 arrayList 一样打印完整的数组。 因此,您可以更改 print 方法的函数签名,如下所示

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

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-27
    • 2014-11-02
    • 1970-01-01
    • 2013-10-11
    • 2011-08-12
    • 2015-10-13
    • 2016-09-26
    相关资源
    最近更新 更多