Java 中的快捷方式“or-assignment”(|=) 运算符

Shortcut "or-assignment" (|=) operator in Java

提问人:David Mason 提问时间:3/21/2010 最后编辑:RaedwaldDavid Mason 更新时间:3/8/2019 访问量:68883

问:

我在 Java 中要做一长串比较,我想知道其中一个或多个是否为真。比较字符串很长且难以阅读,因此为了可读性,我将其分解,并自动使用快捷运算符而不是 .|=negativeValue = negativeValue || boolean

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

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


同样,如果我想执行几个逻辑交集,我是否可以使用 ?&=&&

java 运算符 复合赋值 or 运算符

评论

13赞 Roman 3/21/2010
你为什么不试试呢?
0赞 Dykam 3/21/2010
这是一般的布尔逻辑,而不仅仅是 Java。所以你可以在其他地方查到它。你为什么不试试呢?
16赞 Jon Skeet 3/21/2010
@Dykam:不,这里有特定的行为。Java 可以选择 |= 短路,这样如果 LHS 已经为真,它就不会评估 RHS - 但事实并非如此。
2赞 David Thornley 3/22/2010
@Jon Skeet:短路适用于不存在的算子,但按位或算子的组合形式。||=|=
1赞 David Thornley 3/22/2010
@Jon Skeet:当然,但是短路与其他化合物赋值算子不一致,因为与 不同,通常需要注意的是计算两次(如果重要的话)。在我看来,大的语言行为决策没有,所以我错过了你的观点。|=a |= b;a = a | b;a||=

答:

16赞 Jon Skeet 3/21/2010 #1

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

作为差异的示例,如果为 null,则此代码没问题:text

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

而这不会:

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

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

0赞 Roman 3/21/2010 #2
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale, 
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;

评论

0赞 Jon Skeet 3/21/2010
我想我更喜欢等等。除了这里进行的所有装箱和包装效率低下之外,我发现理解你的代码的真正含义并不容易。negativeValue = defaultStock < 0 || defaultWholesale < 0
0赞 Roman 3/21/2010
我有超过 4 个参数,并且所有参数的标准都相同,那么我喜欢我的解决方案,但为了可读性,我会将其分成几行(即创建列表、查找最小值、将最小值与 0 进行比较)。
0赞 David Mason 3/21/2010
这看起来确实有助于进行大量比较。在这种情况下,我认为清晰度的降低不值得在打字等方面节省。
219赞 polygenelubricants 3/21/2010 #3

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

换言之,对于 ,这两者是等价的:boolean b1, b2

 b1 |= b2;
 b1 = b1 | b2;

逻辑运算符 ( 和 ) 与它们的条件对应运算符 ( 和 ) 的区别在于前者不会“短路”;后者确实如此。那是:&|&&||

  • &始终计算两个操作数|
  • &&有条件地评估正确的操作数;仅当右操作数的值可能影响二进制操作的结果时,才会计算该操作数。这意味着在以下情况下不会评估正确的操作数:||
    • 的左操作数计算结果为&&false
      • (因为无论正确的操作数计算为什么,整个表达式都是false)
    • 的左操作数计算结果为||true
      • (因为无论正确的操作数计算为什么,整个表达式都是true)

所以回到你最初的问题,是的,这个构造是有效的,虽然不完全是 和 的等效快捷方式,但它确实计算了你想要的东西。由于您使用的运算符的右侧是一个简单的整数比较操作,因此不短路的事实是微不足道的。|==|||=|

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

不幸的是,与其他一些语言不同,Java 没有 和 。这在问题中讨论过 为什么 Java 没有条件赋值和条件运算符和条件运算符的复合赋值版本?(&&=, ||=).&&=||=

评论

0赞 Carl 3/22/2010
+1,非常彻底。如果编译器能够确定 RHS 没有副作用,那么它很可能转换为短路运算符,这似乎是合理的。有什么线索吗?
0赞 polygenelubricants 3/22/2010
我读到,当 RHS 微不足道且不需要 SC 时,“智能”SC 操作员实际上会慢一些。如果为真,那么更有趣的是,想知道某些编译器是否可以在某些情况下将 SC 转换为 NSC。
0赞 JAB 6/19/2014
@polygenelubricants短路运算符涉及某种分支,因此,如果与运算符一起使用的真值没有分支预测友好的模式和/或正在使用的体系结构没有良好的/任何分支预测(并且假设编译器和/或虚拟机本身不进行任何相关优化),那么是的,与非短路相比,SC 运算符会引入一些缓慢。找出编译器做任何事情的最好方法是使用 SC 和 NSC 编译程序,然后比较字节码以查看它是否不同。
0赞 user253751 1/27/2015
另外,不要与按位 OR 运算符混淆,后者也是 |
3赞 Peter Lawrey 3/21/2010 #4

你可以只有一个语句。它用多行表示,读起来几乎与示例代码完全相同,只是不那么必要:

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

对于最简单的表达式,使用可能比因为即使它避免进行比较,也意味着隐式使用分支,而这可能要花费很多倍。|||

评论

0赞 David Mason 3/22/2010
我确实只从一个开始,但正如最初的问题所述,我觉得“一连串的比较很长,很难阅读,所以我为了可读性而把它拆开了”。撇开这一点不谈,在这种情况下,我更感兴趣的是学习 |= 的行为,而不是让这段特定的代码工作。
0赞 Farid 12/23/2020
我似乎找不到这个特定运算符的好用例。我的意思是,如果你只关心结果,为什么不停在你的条件得到满足的地方呢?
1赞 Peter Lawrey 12/25/2020
@Farid有时你不想停下来,比如说我想检测许多任务中的任何一个是否做了什么。我有一个标志,仅仅因为我的一个任务做了某事并不意味着我不想运行我的其他任务,但是如果没有任务繁忙,我想采取一个操作。busy
1赞 Carl 3/22/2010 #5

虽然对于您的问题来说可能有点矫枉过正,但 Guava 库有一些很好的 s 语法,并且对 or/和 s 进行短路评估。PredicatePredicate

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

0赞 oneklc 1/12/2013 #6

||逻辑布尔值 OR
|按位 OR

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

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

C |= 2 is same as C = C | 2

java 操作员教程

评论

0赞 user85421 1/12/2013
没有带布尔值的按位或!运算符是整数按位 OR,但也是逻辑 OR - 请参见 Java 语言规范 15.22.2。|
0赞 oneklc 3/21/2013
你是对的,因为对于单个位(布尔值),按位和逻辑 OR 是等效的。虽然实际上,结果是一样的。
1赞 Krzysztof Jabłoński 4/25/2013 #7

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

// 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
});

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

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

方法签名为:

boolean checkIfAnyNegative(DataType ... data)

通话可能如下所示:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

评论

1赞 David Mason 4/26/2013
数组分配和方法调用对于短路来说是一个相当大的成本,除非你在比较中有一些昂贵的操作(尽管问题中的示例很便宜)。话虽如此,大多数时候,代码的可维护性将胜过性能考虑。如果我在一堆不同的地方以不同的方式进行比较或比较超过 4 个值,我可能会使用这样的东西,但对于单一情况,这对我的口味来说有点冗长。
0赞 Krzysztof Jabłoński 4/26/2013
@DavidMason我同意。但是请记住,大多数现代计算器会在不到几毫的时间内吞下这种开销。就我个人而言,在性能问题之前,我不会关心开销,这似乎是合理的。此外,代码的详细程度也是一个优势,尤其是当 javadoc 不是由 JAutodoc 提供或生成时。
1赞 oxyt 4/16/2018 #8

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

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

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

这有什么意义呢?关键是要避免样板并消除重复的代码。

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

b1 |= b2;

评论

1赞 Andrei 3/8/2019
更难发现操作和操作员名称中的错误,尤其是当名称很长时。 与。longNameOfAccumulatorAVariable += 5;longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;