为什么在 Java 子方法中对 Map.Entry<Integer,Integer> 的更新在作为状态传递时不反映在父方法中?[复制]

Why don't updates to Map.Entry<Integer,Integer> in a Java child method reflect in the parent method, when passed around as state? [duplicate]

提问人:Zephyr 提问时间:5/31/2023 更新时间:5/31/2023 访问量:28

问:

我正在尝试解决一个简单的 leetcode 问题。这个问题是一个树问题,要求我们归还最深的叶子总和。https://leetcode.com/problems/deepest-leaves-sum/description/

我使用DFS来解决这个问题。我将一个 level 变量作为递归 DFS 调用的输入之一传递。我在叶节点(基本情况)报告结果(如果级别确实比我们之前看到的更深)。我使用 Map.Entry<Integer, Integer> 来保存结果 - 它将最深的级别映射到其各自的总和。

我注意到的是,当我将此 Map.Entry<> 声明为全局类级变量时,这些方法正确地反映了对它所做的更新。但是当我在父函数中将其初始化为 null 并在 dfs 调用中将其作为状态传递时,尝试在叶节点上更新它 - 更改不会反映在 main 方法中。

我很难理解为什么?

这行得通

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {

    Map.Entry<Integer, Integer> deepestLevelSum; 

    public int deepestLeavesSum(TreeNode root) {
        if (root.left == null && root.right == null)
            return root.val;
        deepestLevelSum = null;
        dfsHelper(root, 0);
        return deepestLevelSum.getValue();
    }

    private void dfsHelper(TreeNode root, int level){
        //report result if valid
        if (root.left == null && root.right == null) {
            if (deepestLevelSum == null || deepestLevelSum.getKey() < level) {
                deepestLevelSum = new HashMap.SimpleEntry(level, root.val);     
            } else if (deepestLevelSum.getKey() == level){
                deepestLevelSum.setValue(deepestLevelSum.getValue()+root.val);
            }
        } else {
            if (root.left != null) 
                dfsHelper(root.left, level+1);
            if (root.right != null)
                dfsHelper(root.right, level+1);
        }
    }
}

但事实并非如此。在 dfs 调用中对 Map.Entry<Integer、Integer> deepestLevelSum 变量所做的更改不会反映在 main(父)方法中。

class Solution {

    public int deepestLeavesSum(TreeNode root) {
        if (root.left == null && root.right == null)
            return root.val;
        Map.Entry<Integer, Integer> deepestLevelSum = new HashMap.SimpleEntry(0, root.val); 
        dfsHelper(root, 0, deepestLevelSum);
        return deepestLevelSum.getValue();
    }

    private void dfsHelper(TreeNode root, int level, Map.Entry<Integer, Integer> deepestLevelSum){
        //report result if valid
        if (root.left == null && root.right == null) {
            if (deepestLevelSum == null || deepestLevelSum.getKey() < level) {
                deepestLevelSum = new HashMap.SimpleEntry(level, root.val);     
            } else if (deepestLevelSum.getKey() == level){
                deepestLevelSum.setValue(deepestLevelSum.getValue()+root.val);
            }
        } else {
            if (root.left != null) 
                dfsHelper(root.left, level+1, deepestLevelSum);
            if (root.right != null)
                dfsHelper(root.right, level+1, deepestLevelSum);
        }
    }
}

据我了解,该条目是可变的 - 这就是为什么我们有 setValue() 方法,但我在对按引用传递的基本理解中是否遗漏了一些东西?

Java 按引用传递

评论


答:

1赞 Sparkling Marcel 5/31/2023 #1

在 Java 中,当您将对象作为参数传递给方法时,您实际上是在将引用的副本传递给该对象。

因此,当您将 deepestLevelSum 重新分配为

deepestLevelSum = new HashMap.SimpleEntry(level, root.val);

 

您正在为方法内的局部 deepestLevelSum 变量分配一个新引用,但它不会影响方法外部的原始引用

return deepestLevelSum.getValue();

不返回预期的值。

最后,你所做的是“覆盖”你拥有的引用副本,而你所期望的是“覆盖”引用指向的对象。

希望这是清楚的

评论

0赞 Zephyr 6/1/2023
我一直认为 Map.Entry 会像包装器对象一样工作,并让我更改值,天哪,我没有意识到我正在使用 deepestLevelSum = new HashMap.SimpleEntry(level, root.val) 对引用本身进行更改。我将方法更改为使用数组或列表,现在工作正常。