3

我正在研究这段代码,但我不明白这一行的作用:[(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));   
                }
            }
        }
4

9 回答 9

6

这是乘以 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 * yy << 3.

因此,我说它是混淆的,因为8 * y更清楚地表达了意图:意图(y << 3) + x是跳过y8 个块,并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我们访问第xth 列,y第 th 行piecesCode。也就是说,y告诉我们要跳过多少个 8 块,并x告诉我们在那个块内去哪里。

于 2013-07-25T15:28:07.677 回答
3

(y << 3)表示向左移位 3 次。这与乘以 2^3 = 8 相同。因此,整个表达式(y << 3) + x变为y * 8 + x.

它应该写成 形式y * 8 + x,因为它更具可读性并且很可能没有性能提升。过早的优化是万恶之源。最好将这种微优化留给编译器(或 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只是迭代具有 8 行和列的(逻辑上)2D 表,存储为 1D,具有 64 个单元格。

最后,我想指出,在给定的代码pieceCodes中是一个字符串数组......但实际上,它是一个片段代码数组。不仅仅是一些字符串。现在,"--"就像某种魔法状态一样,除了程序员之外没有人知道它的含义。if (pieceCode.length() != 2)看起来也很糟糕。所以,应该有一个对象PieceCode和数组将被声明为PieceCode[] pieceCodes. 在PieceCode我们可以实施适当的equals()方法。如果PieceCode只是一个状态,它可以是一个枚举。例如EMPTY, WHITE_PAWN, WHITE_QUEEN, BLACK_PAWN, BLACK_QUEEN. 比较字符串不如比较枚举快。我们还要注意写equals(),而不是==

于 2013-07-25T15:28:47.460 回答
2

从规范:

n << s 的值是 n 个左移 s 位位置;这等效于(即使发生溢出)乘以 s 的 2 次方。

于 2013-07-25T15:30:07.093 回答
2

<< 和 >> 是位移运算符。在这种情况下,它将 y 转换为二进制并“移位”3 个位置,根据需要在末尾添加新位

例如,如果 y 为 8,则其值为 1000

y<<3 将向左移动 3 位,结果为 1000000,即 64

于 2013-07-25T15:31:14.330 回答
2

该代码使用一种优化技术,将二维数组[m][n] 表示为一维数组[m*n]。m 和 n 在这里似乎都是 8(可能是 8 皇后、国际象棋?)。

诀窍是将索引元组 (i,j) 转置为一维数组的索引。

大多数情况下,您通过将 i 乘以 n 并添加 j 来做到这一点。

由于 n=8,在这种情况下,乘法可以通过左移 3 位来表示。这传达了这样一个信息:“我们在这里对一些大小合适的(即,根据 2 的幂)数组进行地址算术。”,至少对于非新手而言。

于 2013-07-25T15:31:55.497 回答
2

这称为按位和位移运算符。另外,请查看wiki

文档摘要

Java 编程语言还提供了对整数类型执行按位和位移位操作的运算符。本节讨论的运算符不太常用。

一元按位补码运算符“~”反转位模式。有符号左移运算符“<<”将位模式左移,有符号右移运算符“>>”将位模式右移。

按位 & 运算符执行按位与运算。

按位 ^ 运算符执行按位异或运算。

按位 | 运算符执行按位包含 OR 运算。

示例代码:

class BitDemo {
    public static void main(String[] args) {
        int bitmask = 0x000F;
        int val = 0x2222;
        // prints "2"
        System.out.println(val & bitmask);
    }
}

那么...什么是按位和位移运算符?

为了节省时间和篇幅,我将简单地包含这篇文章,深入解释所有运算符!

于 2013-07-25T15:35:17.883 回答
1

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

于 2013-07-25T15:29:47.270 回答
1

y << 3 表示“左移 3 位”......本质上,这是另一种执行“* 8”的方法

如果您进行右移(y >> 3),那将是整数除以八,但也很有用,因为位会从末尾脱落,并且如果您循环,您会有点“耗尽”位。

过去(很久以前)CPU 移位比乘法快,所以使用“x << 1”比“x * 2”快。然而,这不再是真的了。

我曾经在代码中看到诸如“x << 4 + x << 2 + x << 1”之类的表达式......实际上是“x * 16 + x * 4 + x * 2”或“x * 22”。

于 2013-07-25T15:29:53.723 回答
1

http://en.wikipedia.org/wiki/Bitwise_operation ... 在 Java 中,所有整数类型都是有符号的,并且“<<”和“>>”运算符执行算术移位。Java添加了运算符“>>>”来执行逻辑右移,但是由于逻辑和算术左移操作是相同的,所以Java中没有“<<<”运算符。

于 2013-07-25T15:35:49.183 回答