簡體   English   中英

迭代鏈表時出現堆棧溢出錯誤

[英]Stack Overflow error on Iterating through linked list

我不是100%確定為什么我在鏈表實現上運行traverse()方法時出現堆棧溢出錯誤。 如果我注釋掉traverse()方法,程序運行正常。

我通過迭代使用size變量並在traverse方法中創建一個計數器變量來進行雙重檢查,但我仍然遇到堆棧溢出錯誤。

例如

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

NodetoString()方法嘗試將其鄰居添加到字符串中。 這意味着它在兩個鄰居上調用toString() 然后,這兩個人都嘗試在他們的鄰居上調用toString() ,包括你開始使用的Node 這是一個無限遞歸。

要避免它,請不要在節點的toString()方法中包含相鄰節點的字符串表示。

問題在於你的Node的toString方法,因為它以遞歸方式來回打印節點:

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

無論何時調用prevNode在String中連接,都會調用其toString 在它上面,將要求初始節點再次打印,因為它將是你的prevNodenextNode ,它將使代碼進入無限遞歸,從而炸毀Stack調用限制。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM