I'm going to assume that you intentionally created a cyclic data structure as an example, and you really do want to be able to generate a String rendering of a cyclic graph.
The answer is not straightforward.
Firstly, you need to perform a traversal of a (potentially) cyclic graph without getting into a loop. The basic algorithm is like this:
public void traverse(List<?> graph) {
doTraverse(graph, new IdentityHashMap<?, ?>());
}
private void doTraverse(List<?> graph, IdentityHashMap<?, ?> visited) {
if (visited.get(graph) == null) {
visited.put(graph, graph);
for (Object element : graph) {
if (element instanceof List<?>) {
doTraverse((List<?>) element, visited);
}
}
}
}
The visited
map allows the traversal to not visit nodes that it has already visited.
The second part of the problem is to modify the code to do the rendering. And at this point you have to decide how you want to render the cycles in the graph ... in the context of the output being a sequence of characters.
The obvious way is to label things. For example we could write a rendering of l1
as
1: [2: [1] ]
(Each list is represented as a [ ... ]
and n:
means that the following thing has a "label" n
. When we encounter a "node" we have already seen, we use a the label; e.g. the 2nd 1
in the above refers back to the list we labelled as 1
.)
Given a representation like that, we can modify the doTravers
method to take an extra StringBuilder
argument, and change the IdentityHashMap
to hold the label strings as values. The rest is pretty straightforward.
The problem with this approach are:
- The rendering is not very readable ... especially for larger and/or more complicated graph structures.
- The algorithm needs to know how to do the traversal AND the rendering for the specific types involved. It is difficult to implement this in a generic, data-structure-independent fashion.