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

时间:2015-06-13 08:34:01

标签: java garbage-collection queue 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;
    }
}

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

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;
    }
}

现在我知道垃圾收集器的工作原理是多么困惑。我听说过,它会清理任何不太重要的参考资料。所以我继续在我的出队课上得到nullpointerexception

 this.tail.setNext(null);

现在,听说垃圾收集器工作,没有什么可以引用它,所以我心里想我的节点是这样设置的

       head          tail
 null<-[1]-><-[2]-><-[3]->null

每个节点可以指向下一个节点,也可以指向上一个节点,所以对于我的队列,我认为我必须做一些事情

1)获取数据(很容易)

2)获取指向前一个

的临时节点
 Node<E> temp = this.tail.getPrev()

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

this.tail.setPrev(null);

因为当我在此之后删除节点时,我无法倒退以删除该引用

       head               tail
 null<-[1]-><-[2]-> null<-[3]->null
 <-[temp]-> ( equals node [2])

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

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吗?还是只有一个?等等,所以任何帮助将不胜感激,谢谢;)

2 个答案:

答案 0 :(得分:5)

您真的不需要关心垃圾收集器的工作原理。如果列表实现正确,那么垃圾收集器将正常运行。

您的NullPointerException将由逻辑错误引起。与垃圾收集无关。

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

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

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

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

一旦功能正确,垃圾收集就可以了。

编辑:

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

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

答案 1 :(得分:0)

您的代码中存在错误。它与垃圾收集器无关。 您得到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()中,检查头是否有一个emtpy队列。但是在方法dequeue()中,你检查尾部是否相同。这可能有点令人困惑。你应该检查两个null。它是对您的计划的额外测试。

构造函数中还有一个错误。 this.size应设置为1而不是0.

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