2017-03-01 4 views
2

リンクリストの実装でtraverse()メソッドを実行しているときにスタックオーバーフローエラーが発生する理由を100%確信していません。私がtraverse()メソッドをコメントアウトすると、プログラムは正常に動作します。リンクリストを反復する際のスタックオーバーフローエラー

サイズ変数を使用して反復処理を行い、トラバースメソッド内でカウンタ変数を作成することによって2回確認されましたが、スタックオーバーフローエラーが発生します。

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

リンクリストクラス

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

} 

Listインタフェース

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

Mainメソッド以下

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()のnullチェックは悪い考えですか?余分な作業が含まれているので、繰り返し呼び出すのは少し高価です。

@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

例外のスタックトレースを投稿してください。 – khelwood

+0

私はスタックトレースを追加しました。 –

+0

注(何も答えが見つからないため):スタックトレースは、LinkedList.javaの79行目で 'toString'の呼び出しを繰り返し表示しています。これは問題を見つけるためのヒントです。 –

答えて

7

にあなたのNodetoString()メソッドは文字列に隣国の両方を追加しようとします。つまり、隣人の両方でtoString()が呼び出されます。両方の人が試して、Nodeを始めとして、両方の隣人にtoString()を呼び出してください。これは無限回帰です。

回避するには、近隣ノードの文字列表現をノードのtoString()メソッドに含めないでください。

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

を使用すると、文字列に連結することがprevNodeを呼び出したときに、そのtoStringが呼び出されます:それは再帰的に、前後にノードを印刷し続けるよう

+0

私はなぜこれを見ていないのか分からない。これを指摘してくれてありがとう! –

+0

@khlewood私は 'toString()'メソッドを書き直しました。 toString()のnullチェックが悪い考えであると思われるかどうかは不思議でした。上記の投稿に私のコードを掲載します。 –

+1

@ J.Leeあなたが 'toString()'で望む振る舞いであれば、これは妥当な方法です。リンクチェックリストは、リンクリストの構造上、必要です。 – khelwood

5

問題は、あなたのノードのtoString方法です。その上で、最初のノードはprevNodenextNodeとなるため、再度印刷するように要求されます。これは、コードが無限再帰を入力し、スタックコールの上限を爆発させます。

+0

ありがとうございます。なぜ私はこれを見ていないのか分からない...それを感謝する! –

関連する問題