【问题标题】:How are expression with logical operators evaluated in Java?如何在 Java 中评估带有逻辑运算符的表达式?
【发布时间】:2021-02-04 10:58:21
【问题描述】:

我一直在尝试在 Java 中实现插入排序,但在我的程序中遇到了一个奇怪的错误。进行插入的while 循环具有以下条件:

while (arr[j] > key && j>=0)

j<0 时,循环因ArrayIndexOutOfBoundsException 而崩溃。我花了几个小时试图解决这个问题,但显然改变表达式的顺序解决了这个问题:

while (j >= 0 && arr[j] > key)

这种行为背后的原因是什么?
这是完整的代码:

//Insertion Sort

class Sort {
    public int[] sort(int[] arr) {
        for (int i = 1; i < arr.length; i++) {
        int j = i - 1;
        int key = arr[i];

        while (arr[j] > key && j>=0) {
            arr[j + 1] = arr[j];
            j = j - 1;
        }
        arr[j + 1] = key;
    }
    return arr;
    }
}

public class InsertionSort {
    public static void main(String[] args) {
        System.out.println("THis is it");
        int[] test = { 54, 68, 92, 3, 565, 8, 7, 64, 0 };
        Sort lort = new Sort();
        lort.sort(test);
        for (int a : test)
            System.out.println(a);
    }
}

【问题讨论】:

  • 您的意思是说第一个版本以ArrayIndexOutOfBoundsException 崩溃而第二个版本没有?这是有道理的。
  • @JohnKugelman 是的,你是对的。

标签: java


【解决方案1】:

在尝试访问之前检查数组索引是否有效很重要。 Java 在右侧之前评估 &amp;&amp; 的左侧。如果您先检查arr[j] &gt; key,那么您不知道j 是否是有效索引,因为这是j &gt;= 0 检查的内容。

当您交换订单并写入j &gt;= 0 &amp;&amp; array[j] &gt; key 时,如果j 为非负数,它将仅访问array[j]。如果为负,则循环立即退出。永远不会访问无效索引,避免异常。

进一步阅读:

【讨论】:

    【解决方案2】:

    布尔运算符(&amp;&amp;||)是“短路的”。这意味着:一旦明确答案已经确定,他们就会立即起身并放弃他们正在做的事情。

    特别是,这意味着 somethingThatEvalsToFalse &amp;&amp; foo 导致 foo 甚至根本没有被评估 - false &amp;&amp; anything 是错误的,因此没有意义。同样适用于somethingThatEvalsToTrue || foo - true || anything 必然为真,因此,foo 无需评估即可提供答案。

    如果foo 有副作用,则这是相关的。 array[j] &gt; key 的副作用是,如果 j 为负数或超过数组的大小,它将抛出 AIOBEx。如果发生短路,则不会评估整个表达式。

    Java 语言规范涵盖了这一点,但我不建议通过阅读语言规范来学习 java。这应该是教程中的一章。

    【讨论】:

      【解决方案3】:
      • j 命中 -1 时:

        while (arr[j] &gt; key &amp;&amp; j&gt;=0)

        评估第一部分,arr[-1] 抛出 ArrayIndexOutOfBoundsException


      • j 命中 -1 时:

        while (j&gt;=0 &amp;&amp; ...)

        永远不会尝试访问arr[-1],因为这是一个AND 运算符,第一个条件为假就足以结束评估。第二个条件无关紧要,因此不在运行时执行。这样可以避免尝试从 Java 数组的 [-1] 位置读取;

        有人说这是被诅咒的操作,Linus Torvalds 将在你的余生中出现在你的梦想中,告诉 C++ 有多糟糕

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-02
        • 1970-01-01
        相关资源
        最近更新 更多