本文主要是介绍再见StackOverFlowError Java Lambda尾递归,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在我们日常开发过程中,难免会遇到需要的递归的业务逻辑开发。
那么它的优点是什么呢?
:代码简单、直观、运算不重复
但是它有一个致命的缺点
:性能得不到保证、计算数据量大时,会造成栈溢出
当我们觉得我们使用递归完成业务后,一运行,发现了StackOverFlowError的错误,严重时会使得整个程序都会崩溃的问题。
我们来举一个简单的例子:
我们算一个数的阶乘
/** 阶乘计算* @param args*/
public static void main(String[] args) {BigInteger n = new BigInteger("20000");System.out.println("Factorial of " + n + " is: " + factorial(n));
}public static BigInteger factorial(BigInteger n) {if (n.compareTo(BigInteger.ONE) == 0) {return n;}return n.multiply(factorial(n.subtract(BigInteger.ONE)));
}
然后我们来运行一下
Exception in thread "main" java.lang.StackOverflowErrorat java.math.BigInteger.compareTo(BigInteger.java:3625)at org.example.Factorial.factorial(Factorial.java:12)at org.example.Factorial.factorial(Factorial.java:15)at org.example.Factorial.factorial(Factorial.java:15)。。。。
报错了,可想而知,在我们的业务中20000的阶乘预算其实并不是很大,就报错了。
那么我们该如何解决这样的错误呢?
我们都知道在Java8中,引入了lamdba函数式编程。
虽然Lambda表达式本身并不是为了递归而设计的,因为Java的Lambda表达式不支持递归调用,但是我们可以使用一些技巧使用Lambda完成递归。
话不多说,我们来实现一下:
1. 首先实现一个为递归的函数,函数内包含以下几个方法:
(这里可以使用java.util.function.Function,为了更好的表达解释,所以我们直观的写一个)
a.递归是否结束方法
b.获取递归结果方法
c.递归循环拼接方法
d.执行递归方法
/*** 尾递归* @param <T>*/
@FunctionalInterface
public interface TailRecursion<T> {/*** 递归执行*/TailRecursion<T> apply();/*** 递归是否结束*/default boolean isFinished() {return false;}/*** 获得递归结果*/default T getResult() {throw new Error("递归还没有结束,调用获得结果异常!");}/*** 执行递归*/default T invoke() {return Stream.iterate(this, TailRecursion::apply).filter(TailRecursion::isFinished).findFirst().get().getResult();}
}
2. 再写一个类,来运行递归和结束递归。
/*** 使用尾递归的类*/
public class TailInvoke {/*** 运行下一个递归*/public static <T> TailRecursion<T> call(final TailRecursion<T> nextFrame) {return nextFrame;}/*** 结束递归*/public static <T> TailRecursion<T> done(T value) {return new TailRecursion<T>() {@Overridepublic TailRecursion<T> apply() {throw new Error("递归已经结束,非法调用apply方法");}@Overridepublic boolean isFinished() {return true;}@Overridepublic T getResult() {return value;}};}
}
最后,我们来改一下计算阶乘的代码,进行运行
/*** 阶乘计算** @param args*/
public static void main(String[] args) {BigInteger n = new BigInteger("80000");BigInteger invoke = factorial(n, n.subtract(new BigInteger("1"))).invoke();System.out.println(invoke);
}public static TailRecursion<BigInteger> factorial(final BigInteger factorial, final BigInteger number) {if (number.compareTo(BigInteger.ONE) == 0) {return TailInvoke.done(factorial);} else {return TailInvoke.call(() -> factorial(factorial.multiply(number),number.subtract(BigInteger.ONE)));}
}
这里我直接使用80000来运算,结果正常输出。
这篇关于再见StackOverFlowError Java Lambda尾递归的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!