【问题标题】:Shortcut "or-assignment" (|=) operator in JavaJava中的快捷“或赋值”(|=)运算符
【发布时间】:2011-01-29 23:56:55
【问题描述】:

我要在 Java 中进行一系列比较,我想知道其中一个或多个是否正确。比较字符串很长且难以阅读,因此为了便于阅读,我将其拆分,并自动使用快捷运算符|= 而不是negativeValue = negativeValue || boolean

boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);

如果任何默认 值为负,我希望negativeValue 为真。这是有效的吗?它会达到我的预期吗?我在 Sun 的网站或 stackoverflow 上看不到它,但 Eclipse 似乎没有问题,并且代码可以编译并运行。


同样,如果我想执行几个逻辑交叉,我可以使用&amp;= 代替&amp;&amp;吗?

【问题讨论】:

  • 你为什么不试试呢?
  • 这是一般的布尔逻辑,不仅仅是 Java。所以你可以在其他地方查找它。你为什么不试试呢?
  • @Dykam:不,这里有特定的行为。 Java可以选择使 |= 短路,这样如果 LHS 已经为真,它就不会评估 RHS - 但它不会。
  • @Jon Skeet:短路适用于不存在的||= 运算符,但|= 是按位或运算符的组合形式。
  • @Jon Skeet: 当然可以,但是让|= 短路会与其他复合赋值运算符不一致,因为a |= b;a = a | b; 不同,通常需要注意的是评估a 两次(如果重要的话)。在我看来,大语言行为决定没有||=,所以我错过了你的意思。

标签: java assignment-operator compound-assignment or-operator


【解决方案1】:

这是一篇旧帖子,但为了给初学者提供不同的视角,我想举个例子。

我认为类似的复合运算符最常见的用例是+=。我相信我们都写过这样的东西:

int a = 10;   // a = 10
a += 5;   // a = 15

这样做的意义何在?重点是避免样板代码并消除重复代码。

因此,下一行的作用完全相同,避免在同一行中输入变量 b1 两次。

b1 |= b2;

【讨论】:

  • 很难发现操作和操作员名称中的错误,特别是当名称很长时。 longNameOfAccumulatorAVariable += 5;longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;
【解决方案2】:

你可以只有一个声明。用多行表示,它读起来几乎和您的示例代码一模一样,只是命令较少:

boolean negativeValue
    = defaultStock < 0 
    | defaultWholesale < 0
    | defaultRetail < 0
    | defaultDelivery < 0;

对于最简单的表达式,使用 | 可能比 || 更快,因为即使它避免进行比较,也意味着隐式使用分支,而且成本可能高出很多倍。

【讨论】:

  • 我确实从一个开始,但正如原始问题中所述,我觉得“比较字符串很长且难以阅读,所以为了便于阅读,我将其拆分”。除此之外,在这种情况下,我更感兴趣的是学习 |= 的行为,而不是让这段特定的代码工作。
  • 我似乎无法为这个特定的运算符找到一个好的用例。我的意思是,如果您只关心结果,为什么不在满足您的条件的地方停下来?
  • @Farid 有时你不想停下来,比如说我想检测是否有任何任务做了什么。我有一个busy 标志,仅仅因为我的一个任务做了某事并不意味着我不想运行我的其他任务,但是如果没有任务很忙,我想采取行动。
【解决方案3】:

如果是关于可读性,我有将测试数据与测试逻辑分离的概念。代码示例:

// declare data
DataType [] dataToTest = new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
}

// define logic
boolean checkIfAnyNegative(DataType [] data) {
    boolean negativeValue = false;
    int i = 0;
    while (!negativeValue && i < data.length) {
        negativeValue = data[i++] < 0;
    }
    return negativeValue;
}

代码看起来更冗长且不言自明。您甚至可以在方法调用中创建一个数组,如下所示:

checkIfAnyNegative(new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
});

它比'比较字符串'更具可读性,并且还具有短路的性能优势(以数组分配和方法调用为代价)。

编辑: 使用可变参数可以简单地实现更高的可读性:

方法签名是:

boolean checkIfAnyNegative(DataType ... data)

调用可能如下所示:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

【讨论】:

  • 数组分配和方法调用对于短路来说是相当大的成本,除非您在比较中进行了一些昂贵的操作(尽管问题中的示例很便宜)。话虽如此,大多数时候代码的可维护性将胜过性能考虑。如果我在一堆不同的地方以不同的方式进行比较或比较超过 4 个值,我可能会使用这样的东西,但对于一个案例来说,这对我的口味来说有点冗长。
  • @DavidMason 我同意。但是请记住,大多数现代计算器会在不到几毫秒的时间内吞下这种开销。就我个人而言,在性能问题之前我不会关心开销,appears to be reasonable。此外,代码冗长也是一个优势,尤其是当 JAutodoc 不提供或生成 javadoc 时。
【解决方案4】:

|= 是布尔逻辑运算符 | (JLS 15.22.2) 的复合赋值运算符 (JLS 15.26.2);不要与条件或|| (JLS 15.24) 混淆。还有&amp;=^=分别对应布尔逻辑&amp;^的复合赋值版本。

换句话说,对于boolean b1, b2,这两个是等价的:

 b1 |= b2;
 b1 = b1 | b2;

逻辑运算符(&amp;|)与条件运算符(&amp;&amp;||)的区别在于前者不会“短路”;后者做。那就是:

  • &amp;| 总是计算两个操作数
  • &amp;&amp;||有条件地评估右操作数;只有当右操作数的值会影响二元运算的结果时,才会计算右操作数。这意味着在以下情况下不评估右操作数:
    • &amp;&amp; 的左操作数计算结果为 false
      • (因为无论右操作数计算为什么,整个表达式都是false
    • || 的左操作数计算结果为 true
      • (因为无论右操作数计算为什么,整个表达式都是true

所以回到你原来的问题,是的,这个构造是有效的,虽然 |= 不完全是 =|| 的等效快捷方式,但它确实计算你想要的。由于您使用的|= 运算符的右侧是一个简单的整数比较运算,因此| 不会短路这一事实无关紧要。

在某些情况下,需要或什至需要短路,但您的情况不是其中之一。

不幸的是,与其他一些语言不同,Java 没有&amp;&amp;=||=。这在问题 Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=) 中进行了讨论。

【讨论】:

  • +1,非常彻底。如果编译器可以确定 RHS 没有副作用,编译器很可能会转换为短路运算符,这似乎是合理的。有什么线索吗?
  • 我读到,当 RHS 很简单并且不需要 SC 时,“智能”SC 运算符实际上会慢一些。如果是真的,那么想知道某些编译器是否可以在某些情况下将 SC 转换为 NSC 会更有趣。
  • @polygenelubricants 短路运算符在底层涉及某种分支,因此,如果与运算符一起使用的真值没有对分支预测友好的模式和/或使用的体系结构确实没有好的/任何分支预测(并且假设编译器和/或虚拟机本身不做任何相关的优化),那么是的,与非短路相比,SC 运算符会引入一些缓慢。找出编译器做任何事情的最佳方法是使用 SC 与 NSC 编译程序,然后比较字节码以查看它是否不同。
  • 另外,不要与按位或运算符混淆,即 also |
【解决方案5】:

||逻辑布尔 OR
|按位或

|= 按位包含 OR 和赋值运算符

|= 不短路的原因是因为它执行按位或而不是逻辑或。 也就是说:

C |= 2 与 C = C | 相同2

Tutorial for java operators

【讨论】:

  • 布尔值没有按位或!运算符 | 是整数位 OR 但也是逻辑 OR - 请参阅 Java Language Specification 15.22.2.
  • 您是正确的,因为对于单个位(布尔值)按位和逻辑 OR 是等效的。虽然实际上,结果是一样的。
【解决方案6】:

尽管对于您的问题来说这可能是多余的,但 Guava 库与 Predicates 有一些不错的语法,并且可以对或/和 Predicates 进行短路评估。

本质上,比较被转换成对象,打包成一个集合,然后迭代。对于 or 谓词,第一个真正的命中从迭代中返回,反之亦然。

【讨论】:

    【解决方案7】:
    List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                           defaultRetail, defaultDelivery);
    int minParam = Collections.min (params);
    negativeValue = minParam < 0;
    

    【讨论】:

    • 我想我更喜欢 negativeValue = defaultStock &lt; 0 || defaultWholesale &lt; 0 等。除了这里进行的所有装箱和包装效率低下之外,我发现理解您的代码的真正含义并不容易.
    • 我他有超过 4 个参数,并且所有参数的标准都是相同的,那么我喜欢我的解决方案,但为了便于阅读,我将它分成几行(即创建列表,找到最小值,将最小值与 0 进行比较。
    • 这对于大量比较来说确实很有用。在这种情况下,我认为清晰度的降低不值得节省打字等。
    【解决方案8】:

    它不是一个“捷径”(或短路)操作符 ||和 && 是(如果他们已经知道基于 LHS 的结果,他们将不会评估 RHS)但它会在工作方面做你想要的。

    作为区别的一个例子,如果text为空,这段代码就可以了:

    boolean nullOrEmpty = text == null || text.equals("")
    

    而这不会:

    boolean nullOrEmpty = false;
    nullOrEmpty |= text == null;
    nullOrEmpty |= text.equals(""); // Throws exception if text is null
    

    (显然你可以为那个特殊情况做"".equals(text) - 我只是想证明这个原则。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-16
      • 2014-04-17
      • 2015-05-02
      • 2015-03-19
      • 1970-01-01
      • 2013-02-14
      相关资源
      最近更新 更多