Это скорее вопрос, который я хотел бы рассказать, чем вопрос: при печати с помощью toString()
Java будет определять прямые циклы в коллекции (где коллекция ссылается на себя), но не косвенные циклы (где коллекция относится к другой коллекции, которая относится к первому - или к большему количеству шагов).Почему цикл Java toString() бесконечно связан с косвенными циклами?
import java.util.*;
public class ShonkyCycle {
static public void main(String[] args) {
List a = new LinkedList();
a.add(a); // direct cycle
System.out.println(a); // works: [(this Collection)]
List b = new LinkedList();
a.add(b);
b.add(a); // indirect cycle
System.out.println(a); // shonky: causes infinite loop!
}
}
Это был настоящий Гоча для меня, потому что это произошло в отладочный код для распечатки коллекции (я был удивлен, когда он поймал прямой цикл, так что я ошибочно предположил, что они выполнили проверку в целом) , Возникает вопрос: почему?
Объяснение, о котором я могу думать, заключается в том, что очень дешево проверять коллекцию, которая ссылается на себя, поскольку вам нужно хранить коллекцию (которая у вас уже есть), но для более длительных циклов вам нужно хранить все коллекции, с которыми вы сталкиваетесь, начиная с корня. Кроме того, вы, возможно, не сможете точно сказать, что такое корень, и поэтому вам нужно будет хранить каждую коллекцию в системе - что вы делаете в любом случае - но вам также нужно будет выполнять поиск хэша на каждом сборный элемент. Это очень дорого для относительно редкого случая циклов (в большинстве программ). (Я думаю), единственная причина, по которой он проверяет прямые циклы, состоит в том, что он настолько дешев (одно сравнение ссылок).
ОК ... Я как-то ответил на свой вопрос - но я пропустил что-то важное? Кто-нибудь хочет что-то добавить?
Разъяснения: теперь я понимаю, проблема, которую я видел, специфична для печати Коллекции (т.е. метода toString()
). Нет проблем с циклами per se (я использую их сам и должен их иметь); проблема в том, что Java не может их распечатать. Редактировать Andrzej Doyle указывает, что это не только коллекции, но и любой объект, вызывающий toString
.
Учитывая, что он ограничивается к этому методу, вот алгоритм проверки для него:
- корня является объектом, который первый
toString()
вызывается (чтобы определить это, необходимо поддерживать состояние на том toString в настоящее время выполняется или нет, поэтому это неудобно).- При перемещении по каждому объекту вы добавляете его в IdentityHashMap вместе с уникальным идентификатором (например, индексом с расширением).
- , но если этот объект уже находится на карте, запишите его идентификатор.
Этот подход также правильно делает multirefs (узел, который упоминается более чем один раз).
Стоимость памяти - это IdentityHashMap (одна ссылка и индекс на объект); стоимость сложности - это хэш-поиск для каждого узла в ориентированном графе (т. е. каждый напечатанный объект).
Typo in «... но не прямые циклы ...», если это не косвенно? –
первый раз, когда я видел кого-то, обернуть System.out.println ... пожалуйста, не обфускайте пример кода (или производственный код!) – basszero
@Stephen: да, спасибо. ysth, похоже, уже исправил это - спасибо. – 13ren