I was just writing some simple utility that calculates the length of a linkedList such that the linkedList doesn't host an "internal" counter of its size/length. With this is mind, I had 3 simple approaches:
- Iterate over the linkedList until you hit the "end"
- Recursively calculate the length
- Recursively calculate the length without needing to return control to the calling function (using some flavor of tail-call recursion)
Below is some code that captures these 3 cases:
// 1. iterative approach
public static <T> int getLengthIteratively(LinkedList<T> ll) {
    int length = 0;
    for (Node<T> ptr = ll.getHead(); ptr != null; ptr = ptr.getNext()) {
        length++;
    }
    return length;
}
// 2. recursive approach
public static <T> int getLengthRecursively(LinkedList<T> ll) {
    return getLengthRecursively(ll.getHead());
}
private static <T> int getLengthRecursively(Node<T> ptr) {
    if (ptr == null) {
        return 0;
    } else {
        return 1 + getLengthRecursively(ptr.getNext());
    }
}
// 3. Pseudo tail-recursive approach
public static <T> int getLengthWithFakeTailRecursion(LinkedList<T> ll) {
    return getLengthWithFakeTailRecursion(ll.getHead());
}
private static <T> int getLengthWithFakeTailRecursion(Node<T> ptr) {
    return getLengthWithFakeTailRecursion(ptr, 0);
}
private static <T> int getLengthWithFakeTailRecursion(Node<T> ptr, int result) {
    if (ptr == null) {
        return result;
    } else {
        return getLengthWithFakeTailRecursion(ptr.getNext(), result + 1);
    }
}
Now I'm aware that the JVM doesn't support tail-recursion out of the box, but when I ran some simple tests that had linkedLists of strings with ~10k nodes, I noticed the getLengthWithFakeTailRecursion consistently outperform the getLengthRecursively method (by ~40%). Could the delta be only attributed to the fact that control is being passed back per node for case#2 and we're forced to traverse across all stack-frames?
Edit: Here's the simple test I used to check the performance numbers:
public class LengthCheckerTest {
@Test
public void testLengthChecking() {
    LinkedList<String> ll = new LinkedList<String>();
    int sizeOfList = 12000;
    // int sizeOfList = 100000; // Danger: This causes a stackOverflow in recursive methods!
    for (int i = 1; i <= sizeOfList; i++) {
        ll.addNode(String.valueOf(i));
    }
    long currTime = System.nanoTime();
    Assert.assertEquals(sizeOfList, LengthChecker.getLengthIteratively(ll));
    long totalTime = System.nanoTime() - currTime;
    System.out.println("totalTime taken with iterative approach: " + (totalTime / 1000) + "ms");
    currTime = System.nanoTime();
    Assert.assertEquals(sizeOfList, LengthChecker.getLengthRecursively(ll));
    totalTime = System.nanoTime() - currTime;
    System.out.println("totalTime taken with recursive approach: " + (totalTime / 1000) + "ms");
    // Interestingly, the fakeTailRecursion always runs faster than the vanillaRecursion
    // TODO: Look into whether stack-frame collapsing has anything to do with this
    currTime = System.nanoTime();
    Assert.assertEquals(sizeOfList, LengthChecker.getLengthWithFakeTailRecursion(ll));
    totalTime = System.nanoTime() - currTime;
    System.out.println("totalTime taken with fake TCR approach: " + (totalTime / 1000) + "ms");
}
}
 
    