博客理由:景区APP项目中涉及到分布式ID设计。分别有几种ID的设计方式:一种是数据库的自增,但是局限很大;第二种uuid,直接采用jdk自带的uuid生成即可;第三种是Twitter的Snowflake(雪花)ID生成技术,目前还在研究公开的源码,但是其中涉及到的移位运算比较多,故写下此文章回顾移位运算。
Java中有三种移位运算:
num << 位数 :这种表示num向左移动多少位,一般会用长整型来描述这个num,例如(Snowflake中的时间戳取值范围)1L << 41,就代表1L毫秒左移41位。
num >> 位数 :num右移具体位数;
>>> : 无符号右移,忽略符号位,空位都以0补齐。
先来看一段移位的Demo,然后我来说明一下如何理解Java的移位运算。
package com.b510.test;/*** @author * @create date:2017-11-17* @version 1.0*/
public class Test {public static void main(String[] args) {int number = 10;//原始数二进制printInfo(number);number = number << 1;//左移一位printInfo(number);number = number >> 1;//右移一位printInfo(number);}/*** 输出一个int的二进制数* @param num*/private static void printInfo(int num){System.out.println(Integer.toBinaryString(num));}
}
运算结果:
1010
10100
1010
这就是移位运算的过程,其实对于Java语言来说,移位运算运用的并不是很多,因此移位运算对于大多数初级开发者来说会感觉到很陌生、很神秘。
我们应当这么理解Java中的移位运算:首先移位运算是针对二进制数来说的,由于Java中的整型(int long)都是以十进制表示,因此我们应当明确一点,一个数可以有多种进制的表达形式,我们写的整型数也同样可以进行移位运算,当然,移位之后的值,就必须去用二进制的思维去考虑了。
一种常见的应用场景是二进制数的取值范围,Snowflake中时间戳用41位的二进制表示,那41位的二进制数可以表示多少个1毫秒呢?计算的方式就是1L << 41,其实这个运算可以拆分成两部分:第一部分1L,第二部分<<41,第一部分代表十进制的1(个)毫秒,第二部分左移41位,实际上在Java底层会将前面的1L转换成二进制数之后再进行移位运算,最后将结果以长整型的形式输出出来。
可以用十进制来类比一下,一个十进制的1左移3位是多少?没错,是1000,取值范围是1~999(一般计算的是整数的取值)。
这样我们就可以理解:为什么Snowflake的41位的时间戳可以用:(1L<< 41) / (1000L * 60 * 60 * 24 * 365) = 69年了。