【问题标题】:Is it OK to use exceptions to check for array boundaries?可以使用异常检查数组边界吗?
【发布时间】:2013-09-23 05:49:06
【问题描述】:

我想检查给定的坐标是否在数组中。

public boolean checkBounds(int x, int y) {
    try {
        Object val = array[x][y];
        return true;
    } catch (ArrayIndexOutOfBoundsException e) {
        return false;
    }
}

我可以那样做吗?这是一种有效的方法吗?

【问题讨论】:

  • 这让我想起了“Effective Java”中的第 57 条:仅在异常情况下使用异常。有一个 example 看起来很像你的 ;-)
  • 在对该问题投反对票之前,请注意这是一个自我回答的问题。

标签: java exception indexoutofboundsexception range-checking


【解决方案1】:

当我们使用异常来执行边界检查时会发生什么?

使用异常来处理空值检查、边界检查、文件存在性检查等操作会在抛出异常时引入大量开销。

如果您只是检查边界,您会做什么:

  • 根据 0 和
  • 检查数组的大小
  • 返回结果

您在使用基于异常的检查时实际在做什么:

  • 检查数组的边界
  • 启动 java 异常机制(及其所有开销)
  • 创建一个新的异常对象
  • 转储整个堆栈跟踪
  • 用所有堆栈数据填充新创建的对象
  • 捕获异常
  • 返回结果

性能对比

通过这个简单的测试程序,我测量了两种数组边界检查的速度。

public class BoundsCheckTest {

    final static int[] array = new int[1];
    final static Random gen = new Random();

    public static void main(String[] args){

        boolean ret = false;
        int tries = 100000000;
        long timestart = System.nanoTime();

        for (int a=0; a< tries; a++) {
            ret = method1();
        }
        long timeend1 = System.nanoTime();
        System.out.println();

        for (int a=0; a< tries; a++) {
            ret = metod2();
        }
        long timeend2 = System.nanoTime();
        System.out.println();


        long t1 = timeend1-timestart;
        long t2 = timeend2-timeend1;
        System.out.println("\ntime 1=["+t1+"]\n     2=["+t2+"]"+
                 "\ndiff=["+Math.abs(t1-t2)+"] percent diff=["+(100d*t2/t1-100)+"]");

    }

    private static boolean metod2() {
        try {
            int val = array[gen.nextInt(2)];
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private static boolean method1() {
        return array.length < gen.nextInt(2);
    }

}

结果:

JDK 7,eclipse Run as 模式:

time check=[911620628]
       exc=[1192569638]
diff=[280949010] percent diff=[30.818632375220886]

JDK 7,eclipse Debug 模式:

time check=[931243924]
       exc=[651480777121]
diff=[650549533197] percent diff=[69858.12378809143]

禁用调试的速度损失不是很明显,尽管它是可见的:没有异常的代码大约快 30%(大约 50% 的错误返回)。调试模式下的速度损失是惊人的。基于异常的代码运行速度比正常的直接数组大小检查慢约 700 倍。

例外哲学

exceptions 背后的总体思路是提供一种处理exceptiona 条件的方法。在这种情况下,根本没有异常情况——范围检查只是代码的正常部分。仅出于这个原因,在这种情况下不应使用异常。

【讨论】:

  • “基于异常的代码的运行速度比普通的直接数组大小检查快 700 倍” 为什么此时要颠倒你的说法?基于异常的代码仍然较慢
  • 有一个用例,比如 100 万次访问中发生一次异常?
  • 编辑:测试过(非常罕见的异常情况,如 1:500.000)=> 与边界检查相比,try catch 明显更快,一旦 JIT 启动,差异就不再那么大了
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
  • 2012-04-21
  • 2011-06-14
  • 2018-04-17
  • 2017-05-08
  • 2014-05-04
  • 1970-01-01
相关资源
最近更新 更多