Java 中 byte[], BitSet, BigInteger 的字节序

Apr 10, 2018

最近在做数据加解密操作, 大部分是关于 bit 位操作, 查找了一下JDK发现一些自带的方便的字节操做方法, 记录一下.

整数进制转换

Integer.toBinaryString(8); Integer.toHexString(95);

还有两个神奇的大类 java.math.BigInteger, java.util.BitSet 有两个方法可以直接处理单个bit位:

boolean BigInteger.testBit(int n) 获取指定的比特位的值 true: 1, false: 0
void BigInteger.setBit(int n)     设置指定的比特位为 1
void BigInteger.clearBit(int n)   设置指定的比特位为 0
boolean BitSet.get(int bitIndex)  获取指定的比特位的值 true: 1, false: 0
boolean BitSet.set(int bitIndex)  设置指定的比特位为 1
boolean BitSet.clear(int bitIndex)设置指定的比特位为 0

基础介绍完了, 我们写个测试用例:

    @Test
    public void bit() {
        byte[] bt = new byte[] { 1, 2, 5, 6, 0, 9, 10, -1, -2 };
        int len = bt.length * 8;
        for (byte b : bt) {
            String bn = "00000000" + Integer.toBinaryString(b);
            System.out.print(String.format("%s ", bn.substring(bn.length() - 8)));
        }
        System.out.println();
        BitSet set = BitSet.valueOf(bt);
        // set.size();
        for (int i = 0; i < len; i++) {
            System.out.print(set.get(i) ? 1 : 0);
            if (i % 8 == 7) {
                System.out.print(" ");
            }
        }
        System.out.println();
        BigInteger big = new BigInteger(1, bt);
        // big.bitLength();
        for (int i = 0; i < len; i++) {
            System.out.print(big.testBit(i) ? 1 : 0);
            if (i % 8 == 7) {
                System.out.print(" ");
            }
        }
        System.out.println();
    }

对字节数组 new byte[] { 1, 2, 5, 6, 0, 9, 10, -1, -2 } 运行结果如下:

00000001 00000010 00000101 00000110 00000000 00001001 00001010 11111111 11111110 
10000000 01000000 10100000 01100000 00000000 10010000 01010000 11111111 01111111 
01111111 11111111 01010000 10010000 00000000 01100000 10100000 01000000 10000000

结果不相同, 奇怪了!

查找 javadoc 提到了 大端(big-endian) 小端(little-endian) 编码, 即字节序列编码方案. BigInteger严格使用了大端(JVM规范, 另外TCP协议要求为大端序列, 所以不会出现网络主机之间的序列错误), BitSet 单字节内使用了大端编码.另外 java也提供了查看系统字节序的类 :ByteOrder 可以尝试使用一下.

查看源码会发现 BigInteger 的源码将字节数组转换为 int[] 来存储, 而 BitSet 使用 ByteBuffer 直接做 JVM 内存的映射, 数组的顺序没有变, 而字节内部的序列为大端.