Java Integer源码剖析

valueOf的实现

下面展示了Integer对象缓存机制的基本原理。Java为了提高性能和减少内存使用,在-128127(包含)这个范围内的整数值上实现了缓存。当调用Integer.valueOf(int i)时,如果这个整数处于缓存范围内,那么就会从缓存中返回相应的Integer实例。

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);
}

IntegerCache是一个私有静态内部类,代码如下

  • static final int low = -128定义缓存的最低值,根据Java规范,至少要缓存从-128到127的整数。
  • static final int high定义缓存的最高值,这个值可以通过JVM参数来配置。
  • static final Integer[] cache定义一个Integer数组,用于存储缓存的Integer对象。

静态初始化方法块中设置默认的缓存上限为127,然后通过sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high")

尝试读取系统属性java.lang.Integer.IntegerCache.high的值,这个值允许用户自定义缓存的上限。如果这个属性被设置了,并且其值大于127,则更新缓存的上限值。

接着将范围内的Integer存入cache,默认情况下保存了-128~127共256个整数对应的Integer对象

最后私有化构造函数,防止外部实例化这个静态内部类。

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对象,只有不在缓存范围时,才通过new创建对象。通过共享常用对象,可以节省内存空间,由于Integer是不可变的,所以缓存的对象可以安全地被共享。

位翻转操作

在Integer中,reversereverseBytes方法通常用于处理整数的位操作,reverse方法用于反转一个整数的所有位,而reverseBytes方法用于反转一个整数的字节顺序。

先看看这两个方法的使用效果:

reverse对于输入的十六进制数 0x12345678(二进制形式为 0001 0010 0011 0100 0101 0110 0111 1000),经过 reverse 方法的操作后,得到的结果为 0x1E6A2C48(二进制形式为 0001 1110 0110 1010 0010 1100 0100 1000),可以看到比特位的顺序发生了逆序排列。

reverseBytes对于原始值0x12345678,其字节顺序为12 34 56 78,反转后变为78 56 34 12(8个二进制位为一个单位,在十六进制中就是2位)

reverse代码实现如下

  1. (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555:交换相邻的奇偶位;
  2. (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333:将每两位元素交换;
  3. (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f:将每四位元素交换;
  4. (i << 24) | ((i & 0xff00) << 8) | ((i >>> 8) & 0xff00) | (i >>> 24):将每八位元素交换。

这四个步骤比较难理解,以12345678这个8位数字为例,

相邻单一数字进行互换,结果为:21436587

以两个数字为一组交换相邻的,结果为:43218765

以4个数字为一组交换相邻的,结果为:87654321,从而实现了位翻转

这样操作是在充分利用CPU便于移位的特性,并行高效地进行相邻位的交换

在给定的位操作 (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555 中,因为0x55555555的二进制形式为0101 0101 0101 0101 0101 0101 0101 0101,每一位上都是交替的 0 和 1,因此它可以用来提取原始数值中的奇数位。通过对原始数值进行与操作,可以提取出所有的奇数位上的比特位。然后将提取出的奇数位左移一位,再通过无符号右移操作符 >>> 将原始数值右移一位,并再次进行与操作提取出偶数位上的比特位。最后通过逻辑或操作 | 将移动后的奇数位和偶数位合并在一起,从而实现了相邻的奇偶位交换的效果。

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;
}

reverseBytes代码实现如下

  1. (i >>> 24):通过无符号右移操作符 >>> 将原始数值右移24位,即将原始数值的最高8位移动到最低8位的位置。
  2. (i >> 8) & 0xFF00:通过右移操作符 >> 将原始数值右移8位,然后与 0xFF00 进行与操作,提取出原始数值中的次高8位,并将其移到次低8位的位置。
  3. (i << 8) & 0xFF0000:通过左移操作符 << 将原始数值左移8位,然后与 0xFF0000 进行与操作,提取出原始数值中的次低8位,并将其移到次高8位的位置。
  4. (i << 24):通过左移操作符 << 将原始数值左移24位,即将原始数值的最低8位移动到最高8位的位置。

最后,将上述四个部分的结果通过逻辑或操作 | 结合起来,得到的结果就是字节顺序逆序排列后的整数

public static int reverseBytes(int i) {
       return ((i >>> 24)           ) |
              ((i >>   8) &   0xFF00) |
              ((i <<   8) & 0xFF0000) |
              ((i << 24));
}

循环移位操作

rotateLeftrotateRight 是Integer位旋转的方法,分别用于将整数的比特位左旋和右旋指定的距离。

对于十六进制数,二进制左右旋8位对应十六进制旋2位

int a = 0x12345678;
int b = Integer.rotateLeft(a, 8);  // 0x34567812
int c = Integer.rotateRight(a, 8); // 0x78123456

对应代码如下:

左旋代码中

  • (i << distance):通过左移操作符 << 将原始数值左移 distance 位,即将原始数值的所有比特位都向左移动 distance 位。
  • (i >>> (32 - distance)):通过无符号右移操作符 >>> 将原始数值右移 32 - distance 位,即将原始数值的所有比特位都向右移动 32 - distance 位。
  • 将上述两个部分的结果通过逻辑或操作 | 结合起来,得到最终的结果。
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);
   }

你可能感兴趣的:(Java常用类的源码剖析,java,算法,开发语言)