Java-for循环使用<<运算符
Posted
技术标签:
【中文标题】Java-for循环使用<<运算符【英文标题】:Java- for loop using << operator 【发布时间】:2013-07-25 15:25:47 【问题描述】:我正在研究这段代码,但我不明白这一行的作用:[(y << 3) + x]
for (int y = 0; y <= 7; ++y)
for (int x = 0; x <= 7; ++x)
final String pieceCode = pieceCodes[(y << 3) + x];
if (pieceCode.length() != 2) throw new IllegalArgumentException();
if (!pieceCode.equals("--"))
pieces[((7 - y) << 3) + x] = CheckersPiece.valueOf(pieceCode.charAt(0), pieceCode.charAt(1));
【问题讨论】:
Bitwise and Shifting operators summary 看起来它正在索引存储在单个数组中的二维数组。 @DaveNewton 我想就是这样! 【参考方案1】:这是乘以 8 的一种混淆方式。因此,(y << 3) + x
等于 8 * y + x
。
y << 3
等于乘以 8 的原因是因为<<
是左移运算符:它将y
的所有位左移一位。以同样的方式,如果你取一个以 10 为底的数字并左移一位,你就有乘以 10,以 2 为底的左移相当于乘以 2。因此,左移三位相当于乘以2 * 2 * 2 = 8。一般情况下,左移n
位置相当于乘以2^n
(只要你没有从左端掉位)。
在过去,程序员编写这样的代码是因为左移速度非常快,比乘法更快,因此8 * y
不如y << 3
最优。但如今,编译器非常擅长确定何时将 8 * y
替换为 y << 3
。
因此,我说它被混淆了,因为8 * y
更清楚地表达了意图:(y << 3) + x
的意图是跳过y
8 个块,并在该块中占据x
th 的位置。这通过8 * y + x
更清楚地表达了更多。请记住,我们使用高级语言编写代码,以便人们阅读和理解代码。我们的代码应该是为人类编写的。编译器可以完成它的工作,制作好的机器指令供机器理解。
这样做是因为它试图假装pieceCodes
是一个二维数组,只是映射到一个一维数组。
即piecesCode
看起来像这样
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
但我们可以假装它看起来像这样
x x x x x x x x
x x x x x x x x
x x x x x x x x
x x x x x x x x
x x x x x x x x
x x x x x x x x
x x x x x x x x
x x x x x x x x
看,给定(x, y) -> 8y + x
,我们访问x
th 列,y
th 行piecesCode
。也就是说,y
告诉我们要跳过多少个 8 块,x
告诉我们在那个块内去哪里。
【讨论】:
不一定是混淆的。 我已经够大了,它没有被混淆:( 具体来说,(y << n) + x
等价于 (y * (2^n)) + x
我说它没有被混淆,因为每个软件工程师都应该像使用十进制系统一样熟悉二进制系统。例如,通过将y << 3
更改为y << 4
来调整相关数组的大小比将8*y
更改为16*y
更自然。那是因为 8 和 16 是写下两个 round 数字的一种非常尴尬的方式。
@Ian,关于围棋板,我同意你的看法。【参考方案2】:
(y << 3)
表示向左移位 3 次。这与乘以 2^3 = 8 相同。因此,整个表达式 (y << 3) + x
变为 y * 8 + x
。
它应该写成y * 8 + x
的形式,因为它更具可读性并且很可能没有性能提升。 Premature optimization is the root of all evil。最好将这种微优化留给编译器(或 JVM)。
此外,电路板大小可以存储在一个常数中,只在一个地方:
final int SIZE = 8;
// ...
for (int y = 0; y < SIZE; y++)
for (int x = 0; x < SIZE; x++)
final String pieceCode = pieceCodes[y * SIZE + x];
y * 8 + x
只是迭代一个(逻辑上)2D 表,该表有 8 行和列,存储为 1D,有 64 个单元格。
最后,我想指出,在给定的代码中,pieceCodes
是一个字符串数组...
但实际上,它是一个片段代码的数组。不仅仅是 一些 字符串。现在,"--"
就像某种魔法状态一样工作,除了程序员之外没有人知道它的含义。 if (pieceCode.length() != 2)
看起来也很糟糕。因此,应该有一个对象PieceCode
,并且数组将被声明为PieceCode[] pieceCodes
。在PieceCode
中,我们可以实现正确的equals()
方法。如果PieceCode
只是一个状态,它可以是一个枚举。例如EMPTY, WHITE_PAWN, WHITE_QUEEN, BLACK_PAWN, BLACK_QUEEN
。比较字符串不如比较枚举快。我们还必须注意写equals()
,而不是==
。
【讨论】:
【参考方案3】:来自规范:
n
【讨论】:
【参考方案4】:> 是位移运算符。在这种情况下,它将 y 转换为二进制并“移位”3 个位置,根据需要在末尾添加新位
例如,如果 y 为 8,则其值为 1000
y
【讨论】:
【参考方案5】:这称为bitwise and bit shift operator。另外,请查看wiki。
文档摘要
Java 编程语言还提供了对整数类型执行按位和位移位运算的运算符。本节讨论的运算符不太常用。
一元按位补码运算符“~”反转位模式。有符号左移运算符“>”将位模式右移。
按位 & 运算符执行按位与运算。
按位 ^ 运算符执行按位异或运算。
按位 |运算符执行按位包含 OR 运算。
示例代码:
class BitDemo
public static void main(String[] args)
int bitmask = 0x000F;
int val = 0x2222;
// prints "2"
System.out.println(val & bitmask);
那么...什么是按位和位移运算符?
为了节省时间和篇幅,我将简单地附上这个article,深入解释所有运算符!
【讨论】:
似乎是一个完全不相关的例子,还有一堆描述其他东西的词。 是的,保持主题是一个愚蠢的想法。对不起。 嘿,不过我只是想帮忙。 是的,这很好。我的观点是,对实际问题的直接解释以及指向进一步支持文档的链接是“正确”的答案。不同意也没关系。 感谢您的关注,因为他得到了答复,现在看来不需要了。【参考方案6】:代码使用一种优化技术,将二维数组[m][n] 表示为一维数组[m*n]。这里的 m 和 n 似乎都是 8(可能是 8-queens,国际象棋?)。
诀窍是将索引元组 (i,j) 转置为一维数组的索引。
大多数时候,你通过将 i 乘以 n 并加上 j 来做到这一点。
由于 n=8,在这种情况下,乘法可以通过左移 3 位来表示。这传达了这样一个信息:“我们正在这里对一些大小合适(即,根据 2 的幂)数组进行地址算术运算。”,至少对于非新手而言。
【讨论】:
我猜是跳棋,因为CheckersPiece
。只是预感。
我不明白为什么每个人都认为换档是由优化驱动的。在我的代码中,为了清楚起见,我使用它:强调所讨论数字的二进制圆度。例如,1<<31
比 2147483638
更简单、更易读、更明显。【参考方案7】:
快速回答,这是将数字乘以 8 (2^3=8) 的有效方法
【讨论】:
为什么高效? 我真的怀疑移位速度是否比乘以 8 快。而且,它的可读性较差。 @DaveNewton 通常,寄存器移位比乘法快得多。但是,(JIT)编译器编写者也知道这一点,并将用最快的操作码序列替换 8*n。也许像load a, n; add a,a; add a,a; add a,a
这样的东西更快。话虽如此,对于我们这些年长的人来说,(a << n) + b
符号非常熟悉和易读,它告诉我们它很可能是一个正在进行的索引操作(而不是一些“数字”计算)。如果他们也学会了这一点,也不会伤害新手。
@Michael.M 我的重点是它是可读的,而且非常可读。你不能说它不可读,因为在印度等国家有数以亿计的高手不理解它。我们为什么要向下调整?他们不能学吗?这不是火箭科学,但是如果您密切关注,您会发现许多“开发人员”对操作系统、机器体系结构、命令行的基本处理等一无所知。一旦他们的 IDE 停止工作,他们就完全脱离了运气。不必如此。
@Ingo 不想让你失望,但也有很多非印度人同样一无所知——这与原籍国无关。【参考方案8】:
y
如果您进行右移 (y >> 3),那将是整数除以 8,但也很有用,因为位会从末尾脱落,并且如果您循环,您会“排出”位。
过去(很久以前)CPU 移位比乘法快,所以使用“x
我曾经在代码中看到诸如“x
【讨论】:
【参考方案9】:http://en.wikipedia.org/wiki/Bitwise_operation ... 在 Java 中,所有整数类型都是有符号的,并且“>”运算符执行算术移位。 Java增加了运算符“>>>”来进行逻辑右移,但是由于逻辑和算术左移操作是相同的,所以Java中没有“
【讨论】:
以上是关于Java-for循环使用<<运算符的主要内容,如果未能解决你的问题,请参考以下文章