为什么在Java中需要使用移位运算符?

23
  1. 使用移位运算符的目的是什么,而不是使用除法和乘法?

  2. 使用移位运算符还有其他好处吗?

  3. 在什么情况下应该尝试使用移位运算符?


2
位移的实际应用,按位运算的实际应用,位运算符的真实世界用例,您是否曾经在实际项目中使用过位移? - phuclv
6个回答

17

除法和乘法并不是真正使用位移操作符。它们是一种过时的“优化”方式,一些人喜欢应用。

它们是位运算,在操作整数值的位级别时是完全必要的。

例如,假设我有两个字节,它们是无符号16位值的高位字节和低位字节。假设您需要构建该值。在Java中,可以这样实现:

int high = ...;
int low = ...;
int twoByteValue = (high << 8) | low;

如果没有位移运算符,你无法做到这一点。

回答你的问题:你在需要使用它们的地方使用它们!其他地方不用。


我听说,它比*和/更快地执行整数除法/乘法运算。 - Saravanan
3
向左移位1比乘以2更快。但是,您的JIT编译器和处理器比您更了解此事,并且应该自动执行。无论如何,这不是移位的主要用途;可以说甚至不是一个好的用途。 - Sean Owen
11
如今甚至不是Java或C支持的特性。编译器足够智能,可以优化您的代码。最好确保您的代码易读且表达其意图,而不是试图超越编译器并使其无法阅读。 - Savvas Dalkitsis
1
移位运算符并不是必需的,只是方便而已,因为左移(右移)n 位相当于乘以(除以)2^n。 - akappa
1
@akappa 其实那是一个相当不错的答案——除了你无法通过除法来实现无符号移位(Java 中的 '>>>')。 - Sean Owen

10

移位运算符用于执行逻辑位操作,而不是数学运算。

当处理的操作数是2的幂时,它可以用于加速,比除法/乘法快得多,但通常清晰的代码优先于原始速度。


2

它在构建数字组合的值时非常有用,其中位被分组为不同的值本身。 (Sean Owen的答案更好地解释了这一点。)

例如,使用颜色:

  • "#AARRGGBB"作为base16字符串
  • 0xAAAARRRRGGGGBBBB作为整数

在其整数格式中,您可以使用移位来获取整数组件的实际值作为可用数字。

public static int stringToColor(String s) throws JSExn {
    // string starts with '#' - parse integer from string
    try {
        // used to build up the return value
        int a, r, g, b;

        switch (s.length()) {
        case 4:
            a = 0xFF000000;
            r = Integer.parseInt(s.substring(1, 2), 16);
            r = r << 16 | r << 20;
            b = Integer.parseInt(s.substring(2, 3), 16);
            b = b << 8 | b << 12;
            g = Integer.parseInt(s.substring(3, 4), 16);
            g = g | g << 4;
            break;
        case 5:
            a = Integer.parseInt(s.substring(1, 2), 16);
            a = a << 24 | a << 28;
            r = Integer.parseInt(s.substring(2, 3), 16);
            r = r << 16 | r << 20;
            b = Integer.parseInt(s.substring(3, 4), 16);
            b = b << 8 | b << 12;
            g = Integer.parseInt(s.substring(4, 5), 16);
            g = g | g << 4;
            break;
        case 7:
            a = 0xFF000000;
            r = Integer.parseInt(s.substring(1, 3), 16) << 16;
            b = Integer.parseInt(s.substring(3, 5), 16) << 8;
            g = Integer.parseInt(s.substring(5, 7), 16);
            break;
        case 9:
            a = Integer.parseInt(s.substring(1, 3), 16) << 24;
            r = Integer.parseInt(s.substring(3, 5), 16) << 16;
            b = Integer.parseInt(s.substring(5, 7), 16) << 8;
            g = Integer.parseInt(s.substring(7, 9), 16);
            break;
        default:
            throw new JSExn("Not a valid color: '"+s+"'");
        }

        // return our integer ARGB
        return a | r | b | g;
}

1

当你处理标志时,将信息存储在一个 int 变量中非常有用,如下所示:

public class DealingWithShiftOperators {

    public static void main(String[] args) {

        int active_flags = 10;

        printActiveFlags(active_flags);

    }

    public static void printActiveFlags(int active_flags) {

        final int TOTAL_FLAGS = 8;
        final int MAX_VALUE = 1 << TOTAL_FLAGS;
        final int MIN_VALUE = 1;

        int current_flag = MAX_VALUE;

        do {
            current_flag = current_flag >> 1;

            if (active_flags - current_flag < 0) {
                System.out.println(current_flag + ": off");
            } else {
                active_flags = active_flags - current_flag;
                System.out.println(current_flag + ": on");
            }

        } while (current_flag > MIN_VALUE);

    }

}

上面的例子将以下内容打印到输出中:
128: off
64: off
32: off
16: off
8: on
4: off
2: on
1: off

你可以看到,active_flags 是数字2和数字8。我们仅使用一个变量来存储这些信息,其值为10(8+2)。

1

当一个操作被替换为执行速度更快的等效操作时,就会发生强度降低。

  1. 用算术移位或逻辑移位代替除以2的幂次方的整数除法或乘法。
  2. 将整数乘以常数替换为移位、加法或减法的组合。
  3. 用乘法代替整数除以常数,利用机器整数的有限范围。

为什么这是错误的?

1. 计算所需的时间增加,降低了性能。 2. 算术运算(如除法和乘法)较慢。 3. 操作开销大。

好处

  1. 提高性能。
  2. 计算速度更快。

缺点

  1. 代码可读性降低。

1

1
链接无法使用。但我找到了另一个页面,解释了相同的概念。 http://crypto.stackexchange.com/questions/19470/how-is-xor-used-for-encryption - shreeneewas

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