使用BitSet与位序的大小端

335 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第17天,点击查看活动详情

1.背景

  你穿越到了2220年,得到了一份火箭研发相关的工作,现在一位硬件工程师"梅人影"给你一个字节的数据,并给出了这个字节对应的各种报警的含义,现在你需要解析这个字节,并得出都有哪些报警。这对于监察火箭实时的情况非常重要,所以你开始阅读对应表格。

1.1.字节对应的表格

位序报警对应0/1报警对应含义
01高温报警
10震动报警
21火箭载货超重
31压力过高报警
40电路短路
51遭遇外星人
61水资源耗尽
70遭遇黑洞

  而且“梅人影”还告诉你,它的这个字节内部的位序是小端序。

2.什么是大小端

小端little-endian)和大端big-endian)。

  • 小端指的是将数据小的一端(即数据低字节)存储在起始地址(即低地址)。

  • 大端指的是将数据大的一端(即数据高字节)存储在起始地址(即低地址)。

由于TCP/IP规定采用大端字节序来传送网络协议数据包中的多字节整数数据,故网络字节序等于大端字节序。

  我们还是举个例子吧,不然永远也说不清。

  假设我们用两个字节来存储十进制数 666 ,那么就会对应 0x029A

  我们可以从高到低去计算这个16进制数进行一下验证:

02×162+9×16+10=66602 \times 16^2 + 9 \times 16 + 10 = 666

  我们可以从低到高去计算这个16进制数进行一下验证:

10+9×16+02×162=66610 + 9 \times 16 + 02 \times 16^2 = 666

  好,现在我们有一个字节数组,需要把 0x029A 存进去,应该怎么存呢?

  负责打印16进制数的工程师说:“应该直接按照咱们人类书写的顺序存,简单直接,我去取的时候,读第0个、第1个,然后打印十六进制数,完美。”

  负责将16进制转换成10进制的工程师说:“别别别,这个数,咱们从低往高存。这样我计算的时候,就可以很快知道当前这一位乘以16的几次方了。”

  双方就这么争吵不休,就像下图这样:

image.png

  于是,双方各自创建了门派,我们把低地址存储数据高字节的 02 9A 一派,称为“大端”派;把低地址存储数据高字节的 9A 02一派,称为“小端”派。

  这就是大小端之争,其实这种分歧,不仅存在于字节序,也存在于位序。

3.什么是位序?

  位序又叫比特序,一个字节中的8个比特位(bit)之间的顺序问题。相应的,也有大小端:

  • 小端指的是将数据小的一端(即数据低比特位)存储在起始地址(即低地址)。

  • 大端指的是将数据大的一端(即数据高比特位)存储在起始地址(即低地址)。

  假设我们有一个比特要存储 68 , 它对应到2进制就是 0100 0100 ,当我们用数组去存储的时候,自然也会有大小端的区别:

image.png

我们从字节序、位序两个例子中发现了,它们都是以最小的单元发生整体的翻转字节序是以字节为单位,位序是以比特位为单位。

4. 解析字节

  好了,现在我们已经弄清楚什么是位序的大端和小端了,又因为位序的大小端正好是相反的,所以我们可以使用java.util.BitSet 来存储一个字节内所有的位:

public static BitSet getBitSetFromOneByte(byte oneByte) {
    BitSet bitSet = new BitSet(8);
    for (int i = 0; i < 8; i++) {
        bitSet.set(i, (oneByte >>> i & 1) > 0);
    }
    return bitSet;
}

  此时我们就可以对照着解析状态了,假设我们需要解析报警“遭遇外星人”,也就是对应:

位序报警对应0/1报警对应含义
51遭遇外星人
if (bitSetFromOneByte.get(5)){
    System.out.println("外星人来啦");
}

  由于它返回的是truefalse,所以很有利于直接判断、流程控制。

总结

  今天我们学习了使用 java.util.BitSet 来进行某个字节的某一位的判断,掘友们,你们有哪些自己工作中的高招呢?欢迎在评论区和大家一起交流。