To string on a collection can get into a infinite loop if somewhere in the graph of collected items is a reference back to itself. See example below.
Yes, good coding practices should prevent this in the first place, but anyway, my question is: What is the most efficient way to detect a recursion in this situation?
One approach is to use a set in a threadlocal, but that seems a bit heavy.
public class AntiRecusionList<E> extends ArrayList<E> {
@Override
public String toString() {
if ( /* ???? test if "this" has been seen before */ ) {
return "{skipping recursion}";
} else {
return super.toString();
}
}
}
public class AntiRecusionListTest {
@Test
public void testToString() throws Exception {
AntiRecusionList<AntiRecusionList> list1 = new AntiRecusionList<>();
AntiRecusionList<AntiRecusionList> list2 = new AntiRecusionList<>();
list2.add(list1);
list1.add(list2);
list1.toString(); //BOOM !
}
}
To prevent infinite recursion, you need at least one branch (i.e. of an if/else statement) that does not make a recursive call. Branches without recursive calls are called base cases; branches with recursive calls are called recursive cases. Functions can also be mutually recursive.
To avoid recursive triggers you can create a class with a static Boolean variable with default value true. In the trigger, before executing your code keep a check that the variable is true or not. Once you check make the variable false.
Infinite Recursion occurs when the recursion does not terminate after a finite number of recursive calls. As the base condition is never met, the recursion carries on infinitely.
This case—when the function completes without making a recursive call—is called the base case. If a recursion never reaches a base case, it will go on making recursive calls forever and the program will never terminate. This is known as infinite recursion, and it is generally not considered a good idea.
When I have to iterate over risky graphs, I usually make a function with a decrementing counter.
For example :
public String toString(int dec) {
if ( dec<=0 ) {
return "{skipping recursion}";
} else {
return super.toString(dec-1);
}
}
public String toString() {
return toString(100);
}
I won't insist on it, as you already know it, but that doesn't respect the contract of toString()
which has to be short and predictable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With