这里有一篇文章讲得很好,转载自 浅谈Java中的递归与尾递归
首先我们讲讲递归
递归的本质是,某个方法中调用了自身。本质还是调用一个方法,只是这个方法正好是自身而已,递归因为是在自身中调用自身,所以会带来以下三个显著特点:
因为上面 2 和 3 两个特点,所以递归调用最大的诟病就是开销巨大,栈帧和堆一起爆掉,俗称内存溢出(一个误区,不是因为调用自身而开销巨大,而是嵌套加上轻易就能无数次调用,使得递归可以很容易开销巨大),既然会导致内存泄露那肯定要想办法了,方法很简单,那就是尾递归优化
尾递归优化
尾递归优化是利用上面的第一个特点 “调用同一个方法” 来进行优化的,包括两个东西:
尾递归的形式
尾递归其实只是一种对递归的特殊写法,这种写法原本并不会带来跟递归不一样的影响,它只是写法不一样而已,写成这样不会有任何优化效果,该爆的栈和帧都还会爆;具体不一样在哪里,前面说了,递归的本质是某个方法调用了自身,尾递归这种形式就要求:某个方法调用自身这件事,一定是该方法做的 最后一件事(所以当有需要返回值的时候会是 return f(n),没有返回的话就直接是 f(n))
要求很简单,就一条,但是有一些常见的误区:
f(n) 外不能加其他东西,因为这就不是最后一件事了,值返回来后还要再干点其他的活,变量空间还需要保留return 3f(n),乘个 n return n*f(n),甚至是 f(n)+f(n-1)编译器对尾递归的优化
上面说了,你光手动写成尾递归的形式,并没有什么卵用,要实现优化,还需要编译器中加入了对尾递归优化的机制,有了这个机制,编译的时候就会自动利用上面的特点一来进行优化,简单说就是重复利用同一个栈帧,不仅不用释放上一个,连下一个新的都不用开,效率非常高(有人做实验,这个比递推比迭代都要效率高)