再见StackOverFlowError Java Lambda尾递归

文章讲述了递归在开发中的优点和性能问题,特别是栈溢出。通过Java8的Lambda表达式和尾递归实现,作者展示了如何避免StackOverflowError,如使用`TailRecursion`接口和`TailInvoke`类进行阶乘计算的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们日常开发过程中,难免会遇到需要的递归的业务逻辑开发。

那么它的优点是什么呢?

:代码简单、直观、运算不重复

但是它有一个致命的缺点

:性能得不到保证、计算数据量大时,会造成栈溢出

当我们觉得我们使用递归完成业务后,一运行,发现了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.StackOverflowError
  at 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>() {
          @Override
          public TailRecursion<T> apply() {
              throw new Error("递归已经结束,非法调用apply方法");
          }

          @Override
          public boolean isFinished() {
              return true;
          }

          @Override
          public 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来运算,结果正常输出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员大猩猩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值