迭代链表

时间:2017-03-01 12:28:42

标签: java linked-list

我不能100%确定为什么在链接列表实现上运行traverse()方法时出现堆栈溢出错误。如果我注释掉traverse()方法,程序运行正常。

我通过迭代使用size变量并在遍历方法中创建一个计数器变量来进行双重检查,但我仍然遇到堆栈溢出错误。

e.g。

@Override
public void traverse() {
    Node<T> data = this.head;       // In order traversal
    int counter = 0;
    while (counter < size) {
        System.out.println(data.toString());
        data = data.getNextNode();
        counter++;
    }
}

Linked List类

public class LinkedList<T extends Comparable<T>> implements List<T> {

    private Node<T> head;           // First element of linked list
    private Node<T> tail;           // Last element of linked list
    private int size;

    public LinkedList() {
        this.size = 0;
    }

    /**
     * TODO: Implement iterator and ForEach methods later on
     * */
    @Override
    public Iterator<T> iterator() {
        return null;
    }

    @Override
    public void forEach(Consumer<? super T> action) {

    }

    @Override
    public Spliterator<T> spliterator() {
        return null;
    }

    private class Node<T> {
        private T data;
        private Node<T> prevNode;
        private Node<T> nextNode;

        public Node(T data) {
            this.data = data;
        }

        public Node(T data, Node<T> nextNode, Node<T> prevNode) {
            this.data = data;
            this.nextNode = nextNode;
            this.prevNode = prevNode;
        }

        public T getData() {
            return data;
        }

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

        public Node<T> getPrevNode() {
            return prevNode;
        }

        public void setPrevNode(Node<T> prevNode) {
            this.prevNode = prevNode;
        }

        public Node<T> getNextNode() {
            return nextNode;
        }

        public void setNextNode(Node<T> nextNode) {
            this.nextNode = nextNode;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "data=" + data +
                    ", prevNode=" + prevNode +
                    ", nextNode=" + nextNode +
                    '}';
        }
    }

    /**
     * Add element to the start of the linked list
     * */
    @Override
    public void add(T data) {
        // head is the first element. If inserted at the front of this list, this will constantly change
        Node<T> node = new Node<>(data, this.head, null);
        if (this.head != null) {
            this.head.setPrevNode(node);
        }

        // Temporarily set new data as the head
        this.head = node;

        if (this.tail == null) {
            this.tail = node;       // If tail is not set, make newly inserted node as tail;
        }
        incrementSize();
    }

    @Override
    public void remove(T data) {
        // TODO
        decrementSize();
    }

    @Override
    public void removeFirst() {
        // TODO
        decrementSize();
    }

    @Override
    public void traverse() {
        Node<T> data = this.head;       // In order traversal
        while (data != null) {
            System.out.println(data.toString());
            data = data.getNextNode();
        }
    }

    @Override
    public int size() {
        return this.size;
    }

    /**
     * ===================================
     * Private methods here
     * */
    private void decrementSize() {
        this.size--;
    }

    private void incrementSize() {
        this.size++;
    }

}

列表界面

public interface List<T> extends Iterable<T> {
    void add(T data);
    void remove(T data);
    void removeFirst();
    void traverse();
    int size();
}

主要方法

public class App {
    public static void main(String[] args) {
        List<Integer> linkedList = new LinkedList<>();
        linkedList.add(10);
        linkedList.add(20);
        linkedList.add(30);

        linkedList.traverse();   // Error here
        System.out.println(linkedList.size());

    }
}

下面是堆栈跟踪

Exception in thread "main" java.lang.StackOverflowError
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at linkedlist.practice.LinkedList$Node.toString(LinkedList.java:79)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at linkedlist.practice.LinkedList$Node.toString(LinkedList.java:79)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at linkedlist.practice.LinkedList$Node.toString(LinkedList.java:79)
at java.lang.String.valueOf(String.java:2994)
at java.lang.StringBuilder.append(StringBuilder.java:131)
at linkedlist.practice.LinkedList$Node.toString(LinkedList.java:79)
at java.lang.String.valueOf(String.java:2994)

感谢您的帮助。在发现对toString()方法的递归调用之后,我将Node内部类中的toString()方法重写为以下内容。 我想知道:toString()中的空检查是个坏主意吗?因为它确实包含了额外的工作,所以重复调用它有点贵。

@Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append( "Node{ data=");
        sb.append(data);
        if (prevNode != null) {
            sb.append(", prevNode=").append(prevNode.getData());
        }
        if (nextNode != null) {
            sb.append(", nextNode=").append(nextNode.getData());
        }
        sb.append('}');
        return sb.toString();
    }

登录控制台

Node{ data=30, nextNode=20}
Node{ data=20, prevNode=30, nextNode=10}
Node{ data=10, prevNode=20}

2 个答案:

答案 0 :(得分:7)

您的Node toString()方法尝试将其邻居添加到字符串中。这意味着它会在其两个邻居上调用toString()。然后,这两个人都尝试在他们的邻居上调用toString(),包括你开始的Node。这是一个无限递归。

为了避免这种情况,请不要在节点的toString()方法中包含相邻节点的字符串表示。

答案 1 :(得分:5)

问题在于您的Node toString方法,因为它会以递归方式来回打印节点:

@Override
public String toString() {
    return "Node{" +
            "data=" + data +
            ", prevNode=" + prevNode +
            ", nextNode=" + nextNode +
            '}';
}

无论何时调用prevNode在字符串中连接,都会调用其toString。在它上面,将要求初始节点再次打印,因为它将是nextNode的{​​{1}},这将使代码进入无限递归,从而炸毁堆栈调用限制。