美文网首页Java 杂谈Java
《Java编程的逻辑》笔记27--剖析包装类 (中)

《Java编程的逻辑》笔记27--剖析包装类 (中)

作者: 天一方蓝 | 来源:发表于2019-07-06 18:27 被阅读10次
    27剖析包装类 (中) 剖析Integer.png

    位翻转

    • 概念
      位翻转就是将int当做二进制,左边的位与右边的位进行互换,reverse是按位进行互换,reverseBytes是按byte进行互换
    • 用法
    public static int reverse(int i) public static int reverseBytes(int i)
    
    • 案例
    int a = 0x12345678;
    System.out.println(Integer.toBinaryString(a));
    
    int r = Integer.reverse(a);
    System.out.println(Integer.toBinaryString(r));
    
    int rb = Integer.reverseBytes(a);
    System.out.println(Integer.toHexString(rb));
    

    输出:

    10010001101000101011001111000
    11110011010100010110001001000
    78563412
    
    • 实现原理
      reverseBytes
      源码
    public static int reverseBytes(int i) {
        return ((i >>> 24)           ) |
               ((i >>   8) &   0xFF00) |
               ((i <<   8) & 0xFF0000) |
               ((i << 24));
    }
    

    过程0x12345678为例
    i>>>24 无符号右移,最高字节挪到最低位,结果是 0x00000012
    (i>>8) & 0xFF00,左边第二个字节挪到右边第二个,i>>8结果是 0x00123456,再进行 & 0xFF00,保留的是右边第二个字节,结果是0x00003400
    (i << 8) & 0xFF0000,右边第二个字节挪到左边第二个,i<<8结果是0x34567800,再进行 & 0xFF0000,保留的是右边第三个字节,结果是0x00560000
    i<<24,结果是0x78000000,最右字节挪到最左边
    这四个结果再进行或操作|,结果就是0x78563412,这样,通过左移、右移、与和或操作,就达到了字节翻转的目的。
    reverse
    源码

    public static int reverse(int i) {
        // HD, Figure 7-1
        i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
        i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
        i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
        i = (i << 24) | ((i & 0xff00) << 8) |
            ((i >>> 8) & 0xff00) | (i >>> 24);
        return i;
    }
    

    思路
    高效实现位翻转的基本思路,首先交换相邻的单一位,然后以两位为一组,再交换相邻的位,接着是四位一组交换、然后是八位、十六位,十六位之后就完成了
    注释指的图

    HD, Figure 7-1.png

    过程
    x & 0x55555555就是取x的奇数位
    x & 0xAAAAAAAA就是取x的偶数位
    x & 0x33333333就是取x以两位为一组的低半部分
    x & 0xCCCCCCCC就是取x以两位为一组的高半部分
    reverse是在充分利用CPU的这些特性,并行高效的进行相邻位的交换

    循环移位

    • 概念
      rotateLeft是循环左移,rotateRight是循环右移,distance是移动的位数,所谓循环移位,是相对于普通的移位而言的,普通移位,比如左移2位,原来的最高两位就没有了,右边会补0,而如果是循环左移两位,则原来的最高两位会移到最右边,就像一个左右相接的环一样
    • 用法
    public static int rotateLeft(int i, int distance)
    
    public static int rotateRight(int i, int distance) 
    
    • 案例
    int a = 0x12345678;
    int b = Integer.rotateLeft(a, 8);
    System.out.println(Integer.toHexString(b));
    
    int c = Integer.rotateRight(a, 8);
    System.out.println(Integer.toHexString(c))
    

    输出:

    34567812
    78123456
    
    • 实现原理
    • 源码
    public static int rotateLeft(int i, int distance) {
        return (i << distance) | (i >>> -distance);
    }
    public static int rotateRight(int i, int distance) {
        return (i >>> distance) | (i << -distance);
    }
    

    这两个函数中令人费解的是负数,如果distance是8,那 i>>>-8是什么意思呢?其实,实际的移位个数不是后面的直接数字,而是直接数字的最低5位的值,或者说是直接数字 & 0x1f的结果。之所以这样,是因为5位最大表示31,移位超过31位对int整数是无效的。
    其最低5位是11000,十进制就是24,所以i>>>-8就是i>>>24,i<<8 | i>>>24就是循环左移8位
    上面代码中,i>>>-distance就是 i>>>(32-distance),i<<-distance就是i<<(32-distance)

    valueOf的实现

    • 源码
    public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];
    
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            }
            high = h;
    
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }
    
        private IntegerCache() {}
    }
    

    IntegerCache表示Integer缓存,其中的cache变量是一个静态Integer数组,在静态初始化代码块中被初始化,默认情况下,保存了从-128到127,共256个整数对应的Integer对象。
    在valueOf代码中,如果数值位于被缓存的范围,即默认-128到127,则直接从IntegerCache中获取已预先创建的Integer对象,只有不在缓存范围时,才通过new创建对象
    所以缓存的对象可以安全的被共享。Boolean/Byte/Short/Long/Character都有类似的实现。这种共享常用对象的思路,是一种常见的设计思路,在<设计模式>这本著作中,它被赋予了一个名字,叫享元模式

    相关文章

      网友评论

        本文标题:《Java编程的逻辑》笔记27--剖析包装类 (中)

        本文链接:https://www.haomeiwen.com/subject/qmlmhctx.html