Почему цикл над пустым списком зависает?

#java

#java

Вопрос:

Просто из любопытства, я написал этот фрагмент кода (я знаю, что это плохой код, я заменил его с тех пор). Это метод toString DocumentTreeNode, который означал, что он рекурсивно вызывался в цикле for (как я уже сказал, плохая идея). Интересно, что он зависал, когда набор дочерних элементов был пуст, кто-нибудь может объяснить, почему это произошло?

Примечание: children является ли TreeSet в данном случае пустым

 public String toString() {
    MoreObjects.ToStringHelper helper =
            MoreObjects.toStringHelper(getClass())
            .add("parent", this.parent)
            .add("key", this.key)
            .add("value", this.value);
    for (DocumentTreeNode<V> child : children.values()) {
        helper = helper.add("child", child);
    }
    return helper.toString();
}
  

Комментарии:

1. Если он действительно пуст, этого не происходит. Расширенный цикл for немедленно обнаружит, что children.values().iterator().hasNext() это false. Я бы ожидал, что на самом деле это непреднамеренный рекурсивный вызов этого toString() метода, т. е. один из this.parent , this.key this.value равно this .

2. Расширенный for цикл над массивом нулевой длины или пустым Iterable не зависает. Управление просто проходит, при этом выполняется нулевое количество итераций тела цикла.

3. На самом деле, я не думаю, что это рекурсивный вызов — который достаточно быстро завершился бы неудачей с StackOverflowError .

4. Являетесь ли вы дочерним элементом своего собственного родителя? Выполняется ли helper.add() вызов toString() второго аргумента? Если да для обоих, у вас есть бесконечный цикл рекурсии прямо здесь. Может занять некоторое время, но в конечном итоге вы получите StackOverflowError .

5. Parent в этом сценарии равен null, случай, который я использовал, имеет только root.

Ответ №1:

Вы добавляете представление parent в toString , которое вызывает его toString . В свою очередь, он вызывает toString своих дочерних элементов, каждый из которых снова вызывает этот метод для родительского элемента. У вас получается бесконечный цикл.

Upd. Хотя я ожидал бы, что это быстро прекратится с чем-то вроде stackoverflow. Я рекомендую вам запустить это в отладчике и пошагово выполнить код для проверки предложенной гипотезы.