关于Java的垃圾收集器如何工作的困惑(节点+队列)

Tan*_*ers 4 java queue garbage-collection nodes

所以我一直在尝试用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在有了节点类,我一直在混淆垃圾收集器的工作方式,所以我们说这是我的队列类

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我得知垃圾收集器如何混淆.我听说过,它会清理任何没有引用的引用.所以我继续在我的dequeue类上得到nullpointerexception

 this.tail.setNext(null);
Run Code Online (Sandbox Code Playgroud)

现在,听说垃圾收集器工作,没有什么可以参考它,所以我想我自己的节点是这样设置的

       head          tail
 null<-[1]-><-[2]-><-[3]->null
Run Code Online (Sandbox Code Playgroud)

每个节点可以指向下一个节点和前一个节点,所以对于我的队列,我想我必须做一些事情

1)获取数据(很容易)

2)获得指向前一个的临时节点

 Node<E> temp = this.tail.getPrev()
Run Code Online (Sandbox Code Playgroud)

3)现在这里是我开始迷路的地方,为了不再引用每个节点,我必须摆脱指向它的所有东西,所以这意味着我必须设置为null

this.tail.setPrev(null);
Run Code Online (Sandbox Code Playgroud)

因为当我删除节点之后,我不能倒退以删除该引用

       head               tail
 null<-[1]-><-[2]-> null<-[3]->null
 <-[temp]-> ( equals node [2])
Run Code Online (Sandbox Code Playgroud)

4)将tail设置为指向temp节点,这是prev节点

this.tail = temp;
Run Code Online (Sandbox Code Playgroud)

不,它应该是这样的

       head   tail    
 null<-[1]-><-[2]->(this still points to [3])    null<-[3]->null
Run Code Online (Sandbox Code Playgroud)

5)但第二个节点仍然指向[3]的存储器地址,所以我继续

this.tail.setNext(null); 
Run Code Online (Sandbox Code Playgroud)

为了使它完全没有引用我们不再存在的任何记忆点,

       head   tail         will be deleted by GC
 null<-[1]-><-[2]->null      null<-[3]->null
Run Code Online (Sandbox Code Playgroud)

但是,NullPointerException当队列中只剩下一个节点时,本部分给出了我的意思.

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

rgh*_*ome 5

你真的不需要关心垃圾收集器是如何工作的.如果列表实现正确,那么垃圾收集器将正常运行.

你的意思NullPointerException是由逻辑错误造成的.与垃圾收集无关.

队列中的头部和尾部引用应引用第一个和最后一个元素.

每个节点都应正确指向上一个和下一个元素.您的逻辑应该识别列表的开头和结尾,并且应该正确地处理节点的插入和删除.

如果从功能的角度来看是正确的,那么删除的节点将不被任何东西引用,垃圾收集器将清理它.

专注于为边缘情况(空列表,一个节点列表)编写单元测试并测试插入和删除操作.

一旦它在功能上正确,垃圾收集将正常工作.

编辑:

在一个长列表中,内部节点将具有前一个和最后一个元素,但是头部和尾部没有,因此您需要特殊的逻辑来处理删除它们.

如果列表有一个元素,则head和tail是相同的,因此head和tail特殊逻辑都将应用于该一个节点.