【问题标题】:In C++ we have references.But in Java how we best "translate" the same code with copy by value?在 C++ 中,我们有引用。但在 Java 中,我们如何最好地“翻译”相同的代码并按值复制?
【发布时间】:2012-03-17 16:36:36
【问题描述】:

可能我只是对什么都感到困惑,但这里是:

在很多情况下,我们有一个递归算法,当我们从一个递归移动到下一个递归时,我们需要的值会被修改。
例如minmaxmaxNoOfNodes等。

在 C++ 中,可以将各种参数作为引用传递,一切正常。

虽然在 Java 中这是行不通的,因为所有内容都是按值复制的,因此必须为参数创建一个额外的类作为持有者,以便在函数内部对其进行修改。
所以这在 C++ 中:

int findLargestSeq(Tree *p, int &min, int &max,Tree *& seqTree)

不能像在 Java 中那样“翻译”,但必须像:
int findLargestSeq(Tree p, Params p)
其中Params 将封装要更新的min max 等。

我想知道这是唯一的方法吗?
对于这种算法,Java 中是否有更“干净”的方法或 标准 模式?

我在想,也许我们修改在 C++ 中作为引用传递的参数这一事实是 C 编程遗留下来的一种习惯,而在纯 OO 的 Java 中,我也坚持以这种“过程”方式思考这一点并且看不到我应该以不同的方式处理这些问题。

非常欢迎任何意见

【问题讨论】:

    标签: java algorithm oop design-patterns recursion


    【解决方案1】:

    在 Java 中,一切都是复制的。然而,除了原语(int、boolean、char 等)之外,其他一切都是指针。

    在 Java 中,如果您想返回多个值,您可以创建一个包含多个值的聚合器类。如果你真的想要一个 void 方法,或者一个给定参数被方法修改后返回结果的方法,你可以使用Holder 模式。

    【讨论】:

    • +1 来自我,但我不喜欢 jax-ws 中使用的 Holder。这对于像 jax-ws 这样的通用框架可能很好,但对于我看不到的算法它的用处。例如,如果我需要修改 OP 中的 min 并且是一个整数,我不想使用一些 generic 持有人
    【解决方案2】:

    当算法复杂到需要递归调用才能修改状态变量时,一个非常简单的解决方案是在您的类中使用私有字段。

    【讨论】:

    • 你的意思是创建算法的对象?
    • 你也可以在同一个类中打包不同的算法或特性,但在某些情况下,你可以给算法一个完整的类。
    • 我想知道你能举一个这种设计的具体例子吗?
    • 迭代器是我想到的第一个例子,即使它们状态的目的有点不同。
    【解决方案3】:

    Can I pass parameters by reference in Java?

    使用 AtomicReference 模板。它看起来又丑又长,但我认为这会给你你正在寻找的行为。

    【讨论】:

      【解决方案4】:

      Java 更喜欢使用对象,它只支持对对象的引用,而不是原语。

      一种重新整合的方法

      int findLargestSeq(Tree *p, int &min, int &max,Tree *& seqTree)
      

      int findLargestSeq(Tree p, int[] min, int[] max,Tree[] seqTree)
      

      可以调用的

      Tree p = ...
      int[] min = { 0 };
      int[] max = { Integer.MAX_VALUE };
      Tree[] seqTree = { tree };
      
      int ret = findLargestSeq(p, min, max, seqTree);
      

      【讨论】:

      • +1,因为它是一种有趣的方法。但是方法声明是否会令读者感到困惑,因为它暗示了一系列值(数组)?而且意图并不明显。即在代码清晰度方面更糟?
      • 我同意,除非您知道 [] 是一种解决方法。我会使用您提到的将它们作为对象传递的方法。
      • 代码演化:从 Lawrey 方法开始。仅当额外的复杂性合理时才使用 user384706 方法。您的代码的读者只会被不必要的复杂性所迷惑。一类“参数”?来吧。如果这就是必要的,程序员为什么不直接使用标准的指针(Lawrey 方法)?另一方面,如果您需要指向泛型类型的指针,则 user384706 方法将是可行的方法。但是程序员应该为这些类使用一个平淡、通用的名字,比如“包装器”。一旦有了自己的metnod,就使用描述性的类名
      【解决方案5】:

      在 Java 中,您传递的不是值的副本,而是引用的副本。

      您不能通过让它指向方法内的另一个对象来更改引用,但是 - 如果对象有修改它的方法 - 修改被引用的对象。

      void failToModify (Foo foo) {
          foo = new Bar (); // Useless
      }
      
      void modify (Foo foo) {
          foo.setFoo (new Bar ()); 
      }
      

      注意,这与数组一致:

      void failToModify (int[] ia) {
          ia = {3, 4, 5}; // No influence for the caller
      }
      
      void modify (int[] ia) {
          ia[0] = 3;
          ia[1] = 4;
          ia[2] = 5;
      }
      

      还有内置类型:

      void failToModify (int a) {
          a = 8; // No influence for the caller
      }
      
      void modifyImpossible (int a) {
          --a; 
          // a build in type is immutable. 
          // You can't modify 8 to be 7.
          // Only the local copy of the reference is decremented.
      }
      
      modifyImpossible (8);
      int a = 7;
      failToModify (a);
      

      您有两种可能性:要么修改参数(如果它不是不可变的),要么返回一个全新的新对象。在递归方法中,您可以逐步传递 sofar-Object(例如 minSofar)。

      【讨论】:

      • 是的,我知道。但这不是我要问的。
      • 你写了since everything is copied by value,这不是正确的表达方式。也许我最后的修改/段落仍然没有回答你的问题 - 它不是最清楚的。
      • 您可能对copy by 表达式的不准确性是正确的。我明白你在说什么,我理解你提到的这些微妙之处。我正在寻找的是是否有一个更清洁/设计处理此类问题的算法的标准方法,而不使用额外的对象作为包装器来解决您在答案中正确提到的问题。现在更清楚了吗?我的意思是与我对 OP 的关注有关?
      • 目前还不清楚“包装器”是什么意思,以及代码的外观,应该被替换。对我来说,不清楚int findLargestSeq (Tree *p, int &min, int &max, Tree *& seqTree) 应该如何在内部工作,以及你想象int findLargestSeq (Tree p, Params p) 的样子。如果你展示一个真正的算法,你可以编写一个替代实现。在目前的状态下,它是一个猜谜游戏。
      • 但是为什么这会让你感到困惑?findLargestSeq 的内部是无关紧要的。相关的是 min maxseqTree 在每个函数调用中都会更新,实际上是函数中的更改会反映给调用者。在 Java 中,min 应该是另一个类的成员,以便调用者“看到”我的任何更新。并且这个类服务器仅作为包装器
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-16
      • 1970-01-01
      • 1970-01-01
      • 2015-05-16
      • 2016-02-03
      • 2022-01-24
      相关资源
      最近更新 更多