This question already has answers here:
What is a NullPointerException, and how do I fix it?

(12 个回答)


6年前关闭。




所以我一直在尝试用 Java 实现 LinkedList、Stack、Queue。

对于我使用的每个节点类,现在我真的不想讨论我的实现方式,因为我知道有更好的方法来做到这一点,我只想专注于我的问题。
public class Node<E> {
    private E data;
    private Node<E> next;
    private Node<E> prev;

    public Node(E data) {
        this.data = data;
        this.next = null;
        this.prev = null;
    }

    public E getData() {
        return this.data;
    }

    public Node<E> getNext() {
        return this.next;
    }

    public Node<E> getPrev() {
        return this.prev;
    }

    public void setPrev(Node<E> prev) {
        this.prev = prev;
    }

    public void setData(E data) {
        this.data = data;
    }

    public void setNext(Node<E> next) {
        this.next = next;
    }
}

现在有了节点类,我一直对垃圾收集器的工作方式感到困惑,所以可以说这是我的队列类
public class Queue<E> {
    private int size;
    private Node<E> head, tail;

    public Queue() {
        this.size = 0;
        this.head = this.tail = null;
    }

    public Queue(E data) {
        Node<E> temp = new Node<E>(data);
        this.tail = this.head = temp;
        this.size = 0;
    }

    public boolean enqueue(E data) {
        Node<E> temp = new Node<E>(data);

        if (this.head == null) {
            this.tail = temp;
            this.head = temp;
        } else {
            temp.setNext(this.head);
            this.head.setPrev(temp);
            this.head = temp;
        }
        this.size++;
        return true;
    }

    public E dequeue() {
        if (this.tail == null)
            throw new IndexOutOfBoundsException();
        else {
            E data = this.tail.getData();
            this.tail.setPrev(null);
            this.tail = temp;
            this.tail.setNext(null);
            this.size--;
            return data;
        }
    }

    public int getSize() {
        return this.size;
    }

    public E peak() {
        if (this.tail == null)
            throw new IndexOutOfBoundsException();
        else
            return this.tail.getData();
    }

    public boolean contains(E data) {
        if (this.head == null)
            return false;
        else {
            for (Node<E> cursor = this.head; cursor != null; cursor = cursor
                    .getNext()) {
                if (cursor.getData().equals(data))
                    return true;
            }
        }
        return false;
    }
}

现在我对垃圾收集器的工作方式感到困惑。我听说,它会清理任何没有被指出的引用。所以我一直在我的 dequeue 类上收到 nullpointerexception
 this.tail.setNext(null);

现在,听说垃圾收集器可以工作,没有什么可以引用它,所以我心里想我的节点是这样设置的
       head          tail
 null<-[1]-><-[2]-><-[3]->null

每个节点都可以指向下一个和上一个节点,所以对于我的出队,我想我必须做的事情很少

1)获取数据(这很容易)

2) 获得一个指向前一个的临时节点
 Node<E> temp = this.tail.getPrev()

3)现在我开始迷路了,为了不再引用每个节点,我必须摆脱指向它的所有东西,所以这意味着我必须将
this.tail.setPrev(null);

因为当我在那之后删除节点时,我无法向后删除该引用
       head               tail
 null<-[1]-><-[2]-> null<-[3]->null
 <-[temp]-> ( equals node [2])

4) 将尾部设置为指向临时节点,即上一个节点所在的节点
this.tail = temp;

不,它应该是这样的
       head   tail
 null<-[1]-><-[2]->(this still points to [3])    null<-[3]->null

5)但是第二个节点仍然指向[3]的内存地址,所以我继续
this.tail.setNext(null);

为了让它完全没有引用我们不再存在的任何内存点,
       head   tail         will be deleted by GC
 null<-[1]-><-[2]->null      null<-[3]->null

然而,当队列中只剩下一个节点时,这部分给了我 NullPointerException

现在,我知道我可能在很多方面都错了,我仍在学习,但我不确定我必须对每个节点做多少事情才能确保垃圾收集器得到它,以便任何帮助都可以做,是吗?需要将 prev 和 next 都设置为 null?还是只有一个?等等,所以任何帮助将不胜感激,谢谢;)

最佳答案

您的代码中存在错误。它与垃圾收集器无关。
您得到 NullPointerException 因为在您的示例中,当队列中只有一个节点时,this.tailnull。您只为一个节点分配 temp = this.tail.getPrev();,它是 null。然后分配 this.tail = temp; 。您将在下面找到 dequeue() 的正确实现。
您不必这样做,但也许有些人会认为这是将所有内容设置为已删除节点中的 null 的好做法。

public E dequeue() {
        if (this.tail == null)
            throw new IndexOutOfBoundsException();
        else {
            E data = this.tail.getData();
            Node<E> temp = this.tail;

            this.tail = temp.getPrev();
            if ( this.tail == null ) { // if that was last node
                this.head = null;
                return data;
            }
            this.tail.setNext(null);

            temp.setPrev(null);
            temp.setNext(null);

            this.size--;
            return data;
        }
    }

enqueue() 方法中,您检查头部是否有空队列。但是在 dequeue() 方法中,您检查 tail 是否相同。这可能有点令人困惑。您可能应该检查两者的 null 。这是对您的程序的额外测试。

构造函数中也有一个错误。 this.size 应该设置为 1 而不是 0。
public Queue(E data) {
        Node<E> temp = new Node<E>(data);
        this.tail = this.head = temp;
        this.size = 1;
    }

关于java - 对 Java 垃圾收集器如何工作的困惑(节点 + 队列),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30816700/

10-11 23:15