使用引用用Java模拟c++指针

Simulating C++ pointers with Java using References?

本文关键字:c++ 指针 模拟 Java 引用      更新时间:2023-10-16

那么,我有一个整数数组

Integer[] distances = new Integer[n];

然后,我有一个节点的优先级队列,其中的结构包含一个"到"answers"距离",可按距离排序。

class Node implements Comparable<Node>{
    Integer distance;
    int to;
    Node(int To, Integer Dist){
      to = To;
      distance = Dist;
    }
    @Override
    public int compareTo(Node o) {
      if(this.distance < o.distance)return -1;
      if(this.distance > o.distance) return 1;
      return 0;
    }
  }

我想做的是像下面这样将节点添加到优先级队列中:

PriorityQueue<Node> q = new PriorityQueue<Node>();
q.add(0, distances[0]);

现在,我想改变dist[0]的值,并在PriorityQueue中的Node对象中得到反映。第一个问题:因为我在构造函数中传递了一个Integer,它是否会通过引用传递,并且具有类似于使用c++指针的功能:

class Node {
  int* distance;
  int to;
  Node(int To, int* dist){
    distance = dist; to = To;
  }

,我创建节点为int[] distances = new int[n]; Node nd = new Node(0, &distances[0]);所以当我改变distances[0]时,我的*nd.distance也会改变。

第二个问题:如果可能的话,这是一个很好的做法,或者是否有任何可能性,在我的优先级队列被处置之前,数组被垃圾收集器处置?

正如在注释中已经指出的那样,您的尝试将失败,因为Integer对象是不可变的。我仍然想发布一个非不可变对象的答案,它将指出与c++指针的关键区别。

第一个问题的简短回答:对于"正常"(非不可变)对象,它将具有类似的功能,但不是完全相同。

在Java中,函数接收对象引用的副本。严格来说,这使得它是按值传递的。下面的示例将演示与c++指针的关键区别。

public class Main {
    public static class Test {
        public String text;
        public Test(String text) {
            this.text = text;
        }
        public void doit() {
            System.out.println(text);
        }
    }
    static void reference() {
        Test[] t = {new Test("This is class 0"), new Test("This is class 1")};
        attemptSwitch(t[0], t[1]);
        System.out.println();
        System.out.println("calling t[0].doit() outside attemptSwitch():");
        t[0].doit();
        System.out.println("calling t[1].doit() outside attemptSwitch():");
        t[1].doit();
        manipulate(t[0]);
        manipulate(t[1]);
        System.out.println();
        System.out.println("calling t[0].doit() after manipulate()");
        t[0].doit();
        System.out.println("calling t[1].doit() after manipulate()");
        t[1].doit();
    }
    static void attemptSwitch(Test t0, Test t1) {
        Test tmp = t0;
        t0 = t1;
        t1 = tmp;
        System.out.println("calling t0.doit() from inside attemptSwitch():");
        t0.doit();
        System.out.println("calling t1.doit() from inside attemptSwitch():");
        t1.doit();
    return;
    }
    public static void manipulate(Test t) {
        t.text = "This class has been manipulated!";
    }
}
上面的类将首先创建一个包含两个Test对象的数组。然后它将尝试切换attemptSwitch()中的这些对象,之后它将尝试通过调用mainpulate()来更改text的内容。输出如下所示:
calling t0.doit() from inside attemptSwitch():
This is class 1
calling t1.doit() from inside attemptSwitch():
This is class 0
calling t[0].doit() outside attemptSwitch():
This is class 0
calling t[1].doit() outside attemptSwitch():
This is class 1
calling t[0].doit() after manipulate()
This class has been manipulated!
calling t[1].doit() after manipulate()
This class has been manipulated!

可以看到,在attemptSwitch()内部的开关是成功的,但是这个开关不是持久的。在函数attemptSwitch()之外,引用完全不受影响,Test对象也没有切换。这是因为attemptSwitch()只接收到Test对象引用的副本。只有那些副本在函数内部被交换。

如对manipulate()的调用所示,可以持久地更改类的内容。在这种情况下,函数接收只是引用的副本并不重要,因为它也指向类存储其内容的相同地址。

关于你的第二个问题,我使用了类似的结构。这种方法可能更容易产生内存泄漏,即在内存中保留对象的时间比需要的时间长,而不是丢失数据。只要至少有一个引用指向你的对象,它就不会被垃圾收集——如果引用不是引用或类似的东西。优先队列使用常规引用