澄清 Java 按值传递进行重新分级

Clarification regrading Java pass-by-value

提问人:Aman Kumar Sinha 提问时间:9/6/2022 最后编辑:Aman Kumar Sinha 更新时间:9/6/2022 访问量:63

问:

我正在解决一个编码问题,我们需要删除只有 0 作为其值的二叉树的所有子树。问题链接 https://leetcode.com/problems/binary-tree-pruning/ 对我有用的解决方案是这样的

public TreeNode pruneTree(TreeNode root) {
    if (root == null)
        return null;
    root.left = pruneTree(root.left);
    root.right = pruneTree(root.right);
    if (root.val == 0 && root.left == null && root.right == null)
        root = null;
    else
        return root;
   // pruneTree1(root);
 //   printTree(root);
    return root;
}

我之前尝试提交的解决方案是这样的

public TreeNode pruneTree(TreeNode root) {
    pruneTree1(root);
    return root;
}

TreeNode pruneTree1 (TreeNode root) {
    if(root ==null)
        return root ;
    root.left = pruneTree1(root.left);
    root.right = pruneTree1(root.right);
    if(root.left==null && root.right==null && root.val==0) {
        System.out.println(root.val);
        root =null;
    }
    return root;
}

我的问题/疑问是为什么第二个解决方案没有改变原来的树。我的理解是 Java 是按值传递的,但是当我们通过变量名传递对象时,它是对原始对象的引用,我们可以更改其内容。

为什么在这种情况下不起作用。是因为我试图将整个对象设置为 null 而不仅仅是它的值吗?

我厌倦了用另一个例子重新创建场景,在这种情况下,代码的行为不同。这是我尝试过的

public void run1() {
        TreeNode root = new TreeNode();
        root.val = 2;
        TreeNode left = new TreeNode();
        left.val = 3;
        TreeNode right = new TreeNode();
        right.val = 4;
        TreeNode leftLeft = new TreeNode();
        leftLeft.val = 5;
        TreeNode rightRight = new TreeNode();
        rightRight.val = 6;
        root.left = left;
        root.right = right;
        left.left = leftLeft;
        right.right = rightRight;
        System.out.println(root.left.left.val);
        TreeNode root2 = makeNull(root);
        System.out.println(root.left.left);
        System.out.println(root2.left.left);

    };

 public   TreeNode  makeNull (TreeNode root){
        if(root ==null)
            return root ;
        root.left = makeNull(root.left);
        root.right = makeNull(root.right);
        if(root.val==5)
            root=null;
        //  left.left = null;
        return root;
    }

在示例中,当我打印它时,root.left.left 和 root2.left.left 都设置为 null。为什么它像参数一样,在这种情况下作为参考传递,但在上面的示例中没有。

Java 按引用传递

评论

2赞 paulsm4 9/6/2022
1)Java总是按值传递。2) 如果参数是对象,则对象的引用按值传递。这意味着被调用的函数可以修改对象的值,并且调用者将看到修改。3) 这也意味着调用方不会看到对引用的任何更改(例如设置“root = null”)。4) 关于解决方案 1 与解决方案 2: 问:您是否单步执行了调试器中的代码?你发现了什么?
0赞 Chris 9/6/2022
注意:帮自己一个忙,使用一致的格式(包括缩进)。这将使你和其他试图帮助你的人更容易。
0赞 tgdavies 9/6/2022
这回答了你的问题吗?Java 是“按引用传递”还是“按值传递”?
0赞 Aman Kumar Sinha 9/6/2022
@tgdavies我之前浏览过这个答案,但无法弄清楚为什么在这种特定情况下它没有按预期工作。我正在第二个解决方案中传递一个对象,因此理想情况下它应该更改原始对象,因为对象变量名称只是对实际对象的引用
1赞 Aman Kumar Sinha 9/6/2022
@AndreasDolk 你的回答对我来说是有道理的,但我想要么这种情况不是问题的测试用例,要么是期望在树只有一个值为 0 的节点时完全删除树。无论如何,我明白你的意思.感谢您指出这一点

答:

0赞 cyberbrain 9/6/2022 #1

在第二个示例中,您没有使用过 的结果。但是,该方法的参数从未为其调用方修改过(由于按值传递)。pruneTree1()

更新已添加示例:

root.left并引用同一对象。因为你也不会改变这一点。root2.leftroot.left.val != 5

您可以将 和 分配给方法的返回值,但在大多数情况下返回输入参数。所以只要你不返回,同样的对象仍然被引用。root.leftroot.rightnull

评论

0赞 Aman Kumar Sinha 9/6/2022
你是对的,这就是我的困惑所在.当我从 pruneTree1 返回(使用)返回对象时,该解决方案有效。但是为什么我需要使用返回的对象。为什么当 prune1 函数的参数(对象变量名称)是对原始对象的引用时,它不会更改原始树本身。@cyberbrain
1赞 cyberbrain 9/6/2022
你没有改变原来的树,这意味着你修改了 or 或 的值。您仅使用调用方不会将该更改视为参数的更改(因为是对对象的引用,该对象由值传递,因此调用方永远不会更改),而仅作为返回值。您可以将其视为调用方具有方法的所有参数的行为 - 这对于被调用方来说是不同的。root.valroot.leftroot.rightrootroot = null;rootfinal