Java-for循环使用<<运算符

Posted

技术标签:

【中文标题】Java-for循环使用<<运算符【英文标题】:Java- for loop using << operator 【发布时间】:2013-07-25 15:25:47 【问题描述】:

我正在研究这段代码,但我不明白这一行的作用:[(y &lt;&lt; 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 &lt;&lt; 3) + x 等于 8 * y + x

y &lt;&lt; 3 等于乘以 8 的原因是因为&lt;&lt; 是左移运算符:它将y 的所有位左移一位。以同样的方式,如果你取一个以 10 为底的数字并左移一位,你就有乘以 10,以 2 为底的左移相当于乘以 2。因此,左移三位相当于乘以2 * 2 * 2 = 8。一般情况下,左移n 位置相当于乘以2^n(只要你没有从左端掉位)。

在过去,程序员编写这样的代码是因为左移速度非常快,比乘法更快,因此8 * y 不如y &lt;&lt; 3 最优。但如今,编译器非常擅长确定何时将 8 * y 替换为 y &lt;&lt; 3

因此,我说它被混淆了,因为8 * y 更清楚地表达了意图:(y &lt;&lt; 3) + x 的意图是跳过y 8 个块,并在该块中占据xth 的位置。这通过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) -&gt; 8y + x,我们访问xth 列,yth 行piecesCode。也就是说,y 告诉我们要跳过多少个 8 块,x 告诉我们在那个块内去哪里。

【讨论】:

不一定是混淆的。 我已经够大了,它没有被混淆:( 具体来说,(y &lt;&lt; n) + x 等价于 (y * (2^n)) + x 我说它没有被混淆,因为每个软件工程师都应该像使用十进制系统一样熟悉二进制系统。例如,通过将y &lt;&lt; 3 更改为y &lt;&lt; 4 来调整相关数组的大小比将8*y 更改为16*y 更自然。那是因为 8 和 16 是写下两个 round 数字的一种非常尴尬的方式。 @Ian,关于围棋板,我同意你的看法。【参考方案2】:

(y &lt;&lt; 3) 表示向左移位 3 次。这与乘以 2^3 = 8 相同。因此,整个表达式 (y &lt;&lt; 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&lt;&lt;312147483638 更简单、更易读、更明显。【参考方案7】:

快速回答,这是将数字乘以 8 (2^3=8) 的有效方法

【讨论】:

为什么高效? 我真的怀疑移位速度是否比乘以 8 快。而且,它的可读性较差。 @DaveNewton 通常,寄存器移位比乘法快得多。但是,(JIT)编译器编写者也知道这一点,并将用最快的操作码序列替换 8*n。也许像load a, n; add a,a; add a,a; add a,a 这样的东西更快。话虽如此,对于我们这些年长的人来说,(a &lt;&lt; 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循环使用<<运算符的主要内容,如果未能解决你的问题,请参考以下文章

Java-for循环练习

java-for循环 vs python-for循环

java-for循环遍历数组

Java - for 循环声明外的逗号运算符

python-判断循环语句

Javascript开发技巧(JS入门运算符分支结构循环结构)