先看一个最典型的递归应用场景:
public class Test4 {
public static Long f(int n){
if(n==1||n==2){
return 1L;
}else{
return f(n-1)+f(n-2);
}
}
public static void main(String[] args) {
long now = System.currentTimeMillis();
System.out.println(f(40));
System.out.println("耗时:"+((System.currentTimeMillis()-now)/1000.0)+"秒");
}
}
程序运行结果:102334155
耗时:4.672秒
如果将计算的个数提高到50的话
程序运行结果:12586269025
耗时:536.292秒
下面使用Stream来完成斐波那契数列的计算工作。
为了使递归计算更加的具有普适性,将从一个接口开始写起:
public interface Recursion<T> {
Recursion<T> compute();
default boolean isFinish(){return false;}
default T result(){throw new UnsupportedOperationException("未实现的方法");}
default T invoke(){
return Stream.iterate(this,Recursion::compute)//试图产生一个无限序列,
//直到某一个Recursion对象通过filter测试
//由于Lambda表达式“天生”的惰性,iterate每执行一次就会去调用filter验证一次
//一旦通过filter验证就会立刻停止执行
.filter(Recursion::isFinish)
.findFirst()
.get()//获得符合条件的Recursion对象
.result();//调用它的result方法获得结果值
}
然后写一个工具类Recursions方便一会儿递归调用发生时Recursion对象的产生
public class Recursions {
public static<T> Recursion<T> call(Recursion<T> recursion){
return recursion;
}
public static<T> Recursion<T> done(T result){
return new Recursion<T>(){
@Override
public Recursion<T> compute() {
throw new UnsupportedOperationException("未实现的操作");
}
@Override
public boolean isFinish() {
return true;
}
@Override
public T result() {
return result;
}
};
}
}
随后可以写一个测试方法和测试类:
public class Test5 {
public static Recursion<Long> f(long l1,long l2,int count){
if(count==1){
return Recursions.done(l1);
}else{
return Recursions.call(f(l2,l1+l2,count-1));
}
}
public static void main(String[] args) {
long now = System.currentTimeMillis();
Long s = f(1, 1, 50).invoke();
System.out.println(s);
System.out.println("耗时:"+((System.currentTimeMillis()-now)/1000.0)+"秒");
}
}
代码运行后:
计算结果为:12586269025
耗时:0.128秒
Test5中与Test4中采用的递归算法并不一样。Test5中的f方法每一次“递归”时都会产生此时此刻的运算结果,然后利用该结果再次参与到下一次运算。
main方法中f(1, 1, 50).invoke(); invoke方法被调用前,f方法执行了50次,并最终返回了一个Recursion对象。注意该Recursion对象由Recursions工具类的done方法创建出来,它的compute方法并未提供具体实现一旦调用就会发生异常。但是当在该Recursion对象上调用invoke方法时,Strema序列iterate产生的无限序列的第一个元素(即Recursion对象本身)就符合filter的条件(该Recursion对象的isComplete方法返回true),因此根本不会通过调用该Recursion对象的compute方法去计算得到第二个序列中的内容,iterate方法就已经结束了。随后就是利用get方法将该Recursion对象从Stream序列中取出后,调用result方法获取计算结果。
本例中的Recursion接口和Recursions工具类在进行递归操作时具有一定的通用性,只要在应用场景中写好适当的测试方法即可,比如做一个累积的递归操作:
public class Test6 {
public static Recursion<Long> f(long l , int count){
if(count==1){
return Recursions.done(l);
}else{
return Recursions.call(f(l*count,count-1));
}
}
public static void main(String[] args) {
System.out.println(f(1,5).invoke());
}
}
程序运行结果为:120