在Java中将布尔值转换为整数而不使用if语句

20

我想知道是否有一种方法可以将布尔值转换为整数,而不使用if语句(以避免破坏流水线)。例如,我可以这样写:

int boolToInt( boolean b ){
  if ( b )
    return 1
  return 0

但我在想是否有一种方法可以不使用if语句来完成,就像Python的

bool = True 
num = 1 * ( bool )

我想你也能做到。

boolean bool = True;
int myint = Boolean.valueOf( bool ).compareTo( false );

不过,这会创建一个额外的对象,所以非常浪费资源,并且我发现它甚至比 if 语句的方式更慢(虽然 if 语句并不一定低效,只是有一个弱点)。


“as not to break the pipeline” 是什么意思? - assylias
1
我认为这篇文章很好地解决了管道问题。 - en_Knight
2
问题是无意义的。在绝对最低层面上,无论使用什么编程语言,事实仍然是会发生一些分支。而这种分支是如此之快,以至于它可以发生10^n次,n <= 10,而你没有时间眨眼。所以,归根结底:为什么这有任何意义呢? - fge
1
我不会将这个作为答案,因为它并不更快,但这里有另一种方法来实现:return (Boolean.valueOf(b).hashCode() >> 1) & 1; Boolean.valueOf 的返回值总是类常量之一,因此没有对象创建开销,但仍涉及两个(隐藏的)if。 - Boann
为什么你使用布尔类型而不是整型?只是好奇,因为我认为布尔类型和整型一样占用内存空间。 - Horse SMith
显示剩余3条评论
12个回答

31

你不能在 if 之外使用布尔值。但是这并不意味着汇编级别上会有分支。

如果你检查该方法的编译代码(顺便一提,使用return b ? 1 : 0;编译出来的指令完全相同),你会发现它没有使用跳转指令:

0x0000000002672580: sub    $0x18,%rsp
0x0000000002672587: mov    %rbp,0x10(%rsp)    ;*synchronization entry
0x000000000267258c: mov    %edx,%eax
0x000000000267258e: add    $0x10,%rsp
0x0000000002672592: pop    %rbp
0x0000000002672593: test   %eax,-0x2542599(%rip)        # 0x0000000000130000
                                                ;   {poll_return}
0x00000000025b2599: retq  
注意:这是在热点服务器7上的结果 - 在不同的虚拟机上可能会得到不同的结果。

注意:这是在热点服务器7上的结果 - 在不同的虚拟机上可能会得到不同的结果。


1
我非常有信心。谢谢! - en_Knight

14

使用 ?: 运算符:( b ? 1 : 0 )


4
我认为三元运算符只是if语句的语法糖。这种说法是否不正确? - en_Knight
1
@mjCSguy 这实际上是一个内联 if 语句,但这只是过于简单化的表述。 - Shadow Man
1
这并没有回答如何避免分支的问题。 - assylias
@mjCSguy 无论您使用 if 还是 ?: 运算符,由于JIT的缘故,JVM最终都很可能优化为相同的运行时字节码,因此... - fge
@assylias 这个问题是,在最低层次上,是否可以避免分支?回答:不行。 - fge
显示剩余4条评论

5
你可以使用三元运算符
return b ? 1 : 0;

如果这被视为一个“if”,并且考虑到这是一个“谜题”,你可以使用以下地图:
return new HashMap<Boolean, Integer>() {{
    put(true, 1);
    put(false, 0);
}}.get(b);

尽管理论上HashMap的实现不需要使用if,但实际上确实需要。然而,“if”不在您的代码中。
当然,为了提高性能,您可以:
private static Map<Boolean, Integer> map = new HashMap<Boolean, Integer>() {{
    put(true, 1);
    put(false, 0);
}};

然后在方法中:

return map.get(b);

8
我认为HashMap是做这件事最慢的可能方式。 - Boann
1
是的,我很感激你可能已经回答了这个问题,但是我将会为基本类型创建对象。实际上,我尝试过了,只是为了看看,但是它非常缓慢,这是可以预料的。 - en_Knight
1
+1 这个 map 解决方案确实回答了这个问题。实际上,它是唯一一个没有 逻辑/命令式 if 的答案,它使用了 函数式 方法。如果 HashMap 是静态的,那么这个解决方案比三元运算符慢 6 倍(1000000 次迭代:6 vs 36ms)。所以在这种情况下它更慢,但一般来说,从某个限制开始(关于您需要映射的值的数量),map/hash 解决方案将比一系列 if 语句更快。 - Beryllium
@Boann 我认为这是一个“谜题”问题,因此性能并不重要。 - Bohemian
@beryllium 把 if 语句隐藏在方法调用后面肯定不是解决方案。而且在 HasMap 代码中,可能会有多个 if 语句。 - Marko Topolnik

4
否则,你可以使用Apache Commons BooleanUtils.toInteger方法,这个方法非常好用...
// Converts a boolean to an int specifying the conversion values.    
static int  toInteger(boolean bool, int trueValue, int falseValue)

// Converts a Boolean to an int specifying the conversion values.
static int  toInteger(Boolean bool, int trueValue, int falseValue, int nullValue)

2
我通过框架找到了解决方案。使用布尔比较。
// b = Your boolean result
// v will be 1 if b equals true, otherwise 0
int v = Boolean.compare(b, false);

它有什么不同?比较实现是 return (x == y) ? 0 : (x ? 1 : -1); - Guy Korland

1

我不能说我推荐这个。它比三元运算符本身慢,而且太聪明了,不能称之为好的编程,但有这个:

-Boolean.FALSE.compareTo(value)

它在内部使用三元运算符(经过几个方法调用后),但不在您的代码中。公平地说,我愿意打赌Python执行中也有某个分支(尽管我可能只打赌五美分;))。

1
这在Java中不是直接可能的。如果你真的需要避免分支,可以考虑直接使用intbyte代替boolean。另外,虚拟机可能足够聪明,在这种情况下消除分支(if?:),因为boolean的内部表示很可能是字面上的1或0。这里有一篇文章介绍了如何检查Oracle JDK生成的本机机器代码,如果您需要速度,请确保使用“服务器”JVM,因为它比“客户端”执行更激进的优化

0
你可以尝试使用三元运算符,像这样:
int value = flag ? 1 : 0;

0
一种合理的避免使用“if”语句的三进制替代方案:
private static Boolean[] array = {false, true};

int boolToInt( boolean b ){
    return Arrays.binarySearch(array, b);
}

请注意,我认为这是一个“谜题”问题,所以如果我自己编码,我会使用三元运算符。

另一个问题只是调用包含if语句的方法。 - Marko Topolnik

0

既然您不想要if/else的解决方案,那么您的表达式是完美的,尽管我会稍微改变一下它。

int myint = Boolean.valueOf( bool ).compareTo( Boolean.FALSE );

不涉及对象创建,Boolean.valueOf(boolean b)返回Boolean.TRUE或Boolean.FALSE,请参阅API


那肯定会在几帧后使用三元运算符。OP似乎关心性能,所以你可能需要提到这一点。此外,您可以少调用一个方法来完成此操作。虽然这并不重要,因为您根本不应该在代码中这样做 ;) - Tim Pote

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接