我在爪哇有一个很长的比较,我想知道他们中的一个或多个是否是真的。比较字符串很长,很难读取,所以为了可读性,我将其拆分,然后自动使用快捷运算符|=,而不是negativeValue = negativeValue || boolean。
boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);
如果任何一个默认值为负数,我希望negativeValue为真。这是有效的吗?它会像我期望的那样吗?我在Sun的网站或StackOverflow上看不到它的相关内容,但Eclipse似乎不存在问题,代码可以编译和运行。
同样,如果我想执行几个逻辑交叉,我可以使用&=而不是&&吗?
你为什么不试试呢?
这是一般的布尔逻辑,而不是Java。所以你可以在其他地方查。你为什么不试试呢?
@戴卡姆:不,这里有具体的行为。Java可以选择进行"短路",这样,如果LHS已经是真的,它就不会评估RHS。
嗯,哎呀,我错过了那个。但是在这个例子中,RHS没有副作用,所以行为在这里除了性能之外,不起作用,是吗?
@jon skeet:短路适用于不存在的||=运算符,但|=是按位或运算符的组合形式。
@大卫:我不是想说应该发生。我只是说这是一个语言行为问题,而不仅仅是布尔逻辑。
@乔恩·斯基特:当然,但是使|=短路会与其他复合赋值运算符不一致,因为a |= b;与a = a | b;不同,通常会对a进行两次评估(如果重要的话)。在我看来,大语言行为决定并不是由||=决定的,所以我没有理解你的观点。
David Thornley:我的第一个评论是驳斥Dykam声称这个问题不属于Java的说法。它是。我完全同意这里的Java设计决定,但这并没有使语言变得更具体。
|=是用于布尔逻辑运算符|(jls 15.22.2)的复合赋值运算符(jls 15.26.2);不要与条件运算符或||混淆(jls 15.24)。还有对应于布尔逻辑&和^的复合赋值版本的&=和^=。
换言之,对于boolean b1, b2,这两个值是相等的:
b1 |= b2;
b1 = b1 | b2;
逻辑运算符(&和|与条件运算符(&&和||相比)的区别在于前者不"短路";后者确实短路。即:
&和|总是评估两个操作数
&&和||有条件地计算右操作数;只有当右操作数的值可能影响二进制运算的结果时,才计算右操作数。这意味着在以下情况下不计算右操作数:
&&的左操作数计算为false。
(因为无论右操作数的计算结果如何,整个表达式都是false)
||的左操作数计算为true。
(因为无论右操作数的计算结果如何,整个表达式都是true)
所以回到你最初的问题,是的,这个构造是有效的,虽然|=并不是=和||的一个等价快捷方式,但它确实计算出你想要的。由于您使用的|=运算符的右侧是一个简单的整数比较运算,因此|不短路的事实是微不足道的。
有些情况下,需要短路,甚至需要短路,但您的场景不是其中之一。
不幸的是,与其他语言不同,Java没有EDCOX1 29和EDCOX1 30。这是在Java为什么没有条件和条件或操作符的复合赋值版本的问题中讨论的?(&;&;=,=)。
+1,非常彻底。如果编译器能够确定rhs没有副作用,那么它很可能转换成短路运算符,这似乎是合理的。有什么线索吗?
我读到,当rhs是微不足道的,sc是不必要的,"智能"sc运营商实际上有点慢。如果是真的,那么更有趣的是想知道在某些情况下,某些编译器是否可以将sc转换为nsc。
@PolygeneLubricants短路运算符涉及到引擎盖下某种类型的分支,因此,如果没有分支预测友好模式,则与运算符和/或使用中的体系结构一起使用的真值没有良好的/任何分支预测(并且假定编译器和/或虚拟机不进行任何相关的优化)当然,SC操作人员会引入一些与非短路相比的慢度。找到编译器的最佳方法是使用sc和nsc编译一个程序,然后比较字节码,看它是否不同。
另外,不要与也是|的按位或运算符混淆。
它不是一个"捷径"(或短路)操作员,就像和&;&;那样(如果他们已经知道基于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)--我只是想证明这个原则。)
你只要一句话。它在多行上表示,读起来几乎与您的示例代码完全相同,只是命令性较低:
boolean negativeValue
= defaultStock < 0
| defaultWholesale < 0
| defaultRetail < 0
| defaultDelivery < 0;
对于最简单的表达式,使用|可能比||更快,因为即使它避免进行比较,它也意味着隐式使用分支,而且可能比使用分支贵很多倍。
我的确是从一个开始,但正如最初的问题所述,我觉得"这串比较很长,很难阅读,所以为了可读性,我把它拆开了"。除此之外,在本例中,我更感兴趣的是学习=的行为,而不是让这段特定的代码工作。
这是一篇老文章,但为了给初学者提供一个不同的视角,我想举个例子。
我认为对于类似的复合操作符最常见的用例是+=。我相信我们都写了这样的东西:
int a = 10; // a = 10
a += 5; // a = 15
这有什么意义?重点是避免样板文件和消除重复的代码。
因此,下一行的操作完全相同,避免在同一行中键入两次变量b1。
b1 |= b2;
在操作和运算符名称中很难发现错误,尤其是在名称较长的情况下。longNameOfAccumulatorAVariable += 5;对longNameOfAccumulatorAVariable = longNameOfAccumulatorVVariable + 5;的比较
如果它是关于可读性的,我已经有了将测试数据与测试逻辑分离的概念。代码示例:
// 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 );
数组分配和方法调用对于短路来说是一个相当大的成本,除非在比较中有一些昂贵的操作(但问题中的示例很便宜)。尽管如此,大多数时候代码的可维护性将超过性能考虑。如果我在一堆不同的地方进行不同的比较,或者比较4个以上的值,我可能会使用类似的方法,但是对于一个例子来说,这对于我的口味来说有点冗长。
@戴维森,我同意。然而,请记住,大多数现代计算器将在不到几毫秒的时间内吞下这种开销。就我个人而言,在性能问题看来是合理的之前,我不会关心开销。另外,代码冗长也是一个优势,特别是当jautodoc没有提供或生成javadoc时。
尽管对于您的问题来说,它可能是多余的,但guava库与Predicates有一些很好的语法,并对or/and Predicates进行短路评估。
本质上,比较被转换成对象,打包成一个集合,然后迭代。对于或谓词,第一个真正命中从迭代返回,反之亦然。
||逻辑布尔或按位或
|=按位包含或与赋值运算符
=不短路的原因是它执行的是位或非逻辑或。这就是说:
C |= 2 is same as C = C | 2
Java操作员教程
没有位或布尔值!运算符EDCOX1〔1〕是整数位的,但也是逻辑的,或参见Java语言规范1522.2。
您是正确的,因为对于单个位(布尔值),位和逻辑或是等价的。实际上,结果是一样的。
List params = Arrays.asList (defaultStock, defaultWholesale,
defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;
我想我更喜欢negativeValue = defaultStock < 0 || defaultWholesale < 0等。除了这里所进行的所有装箱和包装的效率低下之外,我发现理解您的代码真正意味着什么几乎没有那么容易。
他有4个以上的参数,所有参数的标准都是一样的,所以我喜欢我的解决方案,但是为了可读性,我将它分成几行(即创建列表、查找minvalue、将minvalue与0进行比较)。
对于大量的比较来说,这确实很有用。在这种情况下,我认为降低清晰度不值得节省打字等费用。