002_01_java.lang.Integer

这回来聊一聊java里面的基础类型,总共有8种,其中6种是数字类型。6中数字类型中Integer是最为关键的类。Short/Byte的实现比较简单,实现同Integer几乎一样,在这里就不做过多解释。

java.lang.Number

6种数字类型都有一个Number的父类,表示他们都是数字型对象。其继承关系如下:
image.png
Number类的源码也很简单,就是负责类型与类型之间的转换操作:

public abstract class Number implements java.io.Serializable {

    public abstract int intValue();
    public abstract long longValue();
    public abstract float floatValue();
    public abstract double doubleValue();
    public byte byteValue() {
        return (byte)intValue();
    }
    public short shortValue() {
        return (short)intValue();
    }

    private static final long serialVersionUID = -8742448824652078965L;
}

java.lang.Integer

java.lang.Integer继承结构如下,其中Comoarable接口我们后面再说。
image.png

重要属性

public static final int   MIN_VALUE = 0x80000000;
public static final int   MAX_VALUE = 0x7fffffff;
public static final int SIZE = 32;
public static final int BYTES = SIZE / Byte.SIZE;
public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
final static char [] DigitTens = {
        '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
        '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
        '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
        '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
        '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
        '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
        '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
        '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
        '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
        '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
        } ;

final static char [] DigitOnes = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        } ;

final static char[] digits = {
        '0' , '1' , '2' , '3' , '4' , '5' ,
        '6' , '7' , '8' , '9' , 'a' , 'b' ,
        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
    };
final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                      99999999, 999999999, Integer.MAX_VALUE };
  1. MIN_VALUE静态变量表示int能取的最小值,为-2的31次方
  2. 类似的还有MAX_VALUE,表示int最大值为2的31次方减1
  3. SIZE用来表示二进制补码形式的int值的比特数,值为32
  4. BYTES用来表示二进制补码形式的int值的字节数,值为SIZE除于Byte.SIZE,结果为4。
  5. TYPE的toString的值是int。

  6. DigitTens和DigitOnes两个数组主要用于获取0到99之间某个数的十位和个位,比如48,通过DigitTens数组直接取出来十位为4,而通过DigitOnes数组取出来个位为8。
  7. digits数组用于表示数字的所有可能的字符,因为int支持从2进制到36进制,所以这里需要有36个字符才能表示所有不同进制的数字。
  8. sizeTable数组主要用在判断一个int型数字对应字符串的长度。比如相关的方法如下,这种方法可以高效得到对应字符串长度,避免了使用除法或求余等操作。
static int stringSize(int x) {
      for (int i=0; ; i++)
          if (x <= sizeTable[i])
              return i+1;
}

重要内部类

private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}

IntegerCache是Integer的一个内部类,它包含了int可能值的Integer数组,默认范围是[-128,127],当Integer的值范围在这个范围的时候则直接从缓存中获取对应的Integer对象。触发这个逻辑可以有以下几种方式:

// 第一种: 字节码将会转化为 Integer i = Integer.valueOf(1);
Integer i = 1;

// 第二种:直接调用Integer.valueOf方法
Integer i = Integer.valueOf(1);

另外我们可以改变这些值缓存的范围,再启动JVM时通过-Djava.lang.Integer.IntegerCache.high=xxx就可以改变缓存值的最大值,比如-Djava.lang.Integer.IntegerCache.high=500则会缓存[-128,500]。

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

主要方法

从字符串到数值转换

parseInt方法
public static int parseInt(String s) throws NumberFormatException {
    return parseInt(s,10);
}

public static int parseInt(String s, int radix) throws NumberFormatException {
    if (s == null) {
        throw new NumberFormatException("null");
    }

    if (radix < Character.MIN_RADIX) {
        throw new NumberFormatException("radix " + radix +
                                        " less than Character.MIN_RADIX");
    }

    if (radix > Character.MAX_RADIX) {
        throw new NumberFormatException("radix " + radix +
                                        " greater than Character.MAX_RADIX");
    }

    int result = 0;
    boolean negative = false;
    int i = 0, len = s.length();
    int limit = -Integer.MAX_VALUE;
    int multmin;
    int digit;

    if (len > 0) {
        // 取出第一个字符
        char firstChar = s.charAt(0);
        // 判定第一个字符是否 是 + 或者 -
        if (firstChar < '0') {
            if (firstChar == '-') {
                negative = true;
                limit = Integer.MIN_VALUE;
            } else if (firstChar != '+')
                throw NumberFormatException.forInputString(s);

            if (len == 1) 
                throw NumberFormatException.forInputString(s);
            i++;
        }

        // 当输入字符串是负数,则limit = -2147483648 否则为 -2147483647 用来控制溢出
        // 举个例子,假设limit = -2147483648 ,radio = 10, 则 multmin = -214748364
        // 意味着,如果输入是-2147483649,下方result < limit + digit 会变成
        // -214748364 * 10 < -2147483648 + 9 返回true,则进入溢出逻辑,抛出异常
        multmin = limit / radix;
        while (i < len) {
            // Character.digit() 用于将字符转为为对应进制的整数,
            // 如果该字符不是进制内的就返回-1
            digit = Character.digit(s.charAt(i++),radix);
            if (digit < 0) {
                throw NumberFormatException.forInputString(s);
            }
            if (result < multmin) {
                throw NumberFormatException.forInputString(s);
            }
            // 原先的结果先乘以进制
            result *= radix;
            if (result < limit + digit) {
                throw NumberFormatException.forInputString(s);
            }
            // 变成负数相加
            result -= digit;
        }
    } else {
        throw NumberFormatException.forInputString(s);
    }
    return negative ? result : -result;
}

两个parseInt方法,主要看第二个即可。其中第一个参数是待转换的字符串,第二个参数表示进制数。例如:

// 表示十进制的100,所以值为100
Integer.parseInt("100",10);
// 表示二进制的100,所以值为4
Integer.parseInt("100",2);
// 超出范围则抛出java.lang.NumberFormatException异常
Integer.parseInt("10000000000",10)

具体怎么做呢?这里简单的描述下一般的字符串处理办法,例如输入“123”,进制是10。那么会有以下处理步骤:
(1)取出1,乘以10,最终结果10 + 1 = 010 + 1
(2)取出2,乘以10,最终结果10 + 2 = 110 + 2
(3)取出3,乘以10, 最终结果10 + 3 = 1210 + 3
回到源码,该方法的逻辑:
(1)判断字符串不为空且进制数在Character.MIN_RADIX和Character.MAX_RADIX之间,即2到36。
(2)判断输入的字符串的长度必须大于0,再根据第一个字符可能为数字或负号或正号进行处理。
(3)再进入核心的处理逻辑,上文提到的是使用加法来做,但是源码实际上使用负数运算,这么做是归咎到int类型的范围,负数Integer.MIN_VALUE变化为正数时会导致数值溢出,所以全部都用负数来运算。

to*String() 进制转换

将数字转换为进制字符串有三个方法:

  • toBinaryString� 转换为2进制字符串
  • toOctalString方法 转换为8进制字符串
  • toHexString 转换为16进制字符串

可以看到都是间接调用toUnsignedString0方法,该方法会先计算转换成对应进制需要的字符数,然后再通过formatUnsignedInt方法来填充字符数组。

public static String toBinaryString(int i) {
    return toUnsignedString0(i, 1);
}

public static String toOctalString(int i) {
    return toUnsignedString0(i, 3);
}

public static String toHexString(int i) {
    return toUnsignedString0(i, 4);
}

private static String toUnsignedString0(int val, int shift) {
    // (1)
    int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
    // (2) 
    int chars = Math.max(((mag + (shift - 1)) / shift), 1);
    char[] buf = new char[chars];

    formatUnsignedInt(val, shift, buf, 0, chars);

    return new String(buf, true);
}

static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
    int charPos = len;
    // (3)
    int radix = 1 << shift;
    int mask = radix - 1;
    do {
        buf[offset + --charPos] = Integer.digits[val & mask];
        val >>>= shift;
    } while (val != 0 && charPos > 0);

    return charPos;
}

(1)Integer.numberOfLeadingZeros 是获取val的前置多少个0, mag表达val除去前置0剩下的位数
(2)n位二进制能表达的最大数是 2^n -1, 存在x的进制,y位的数据,要求后者的取值范围大于前者:
则需要满足不等式 2 n − 1 < = x y − 1 2^n - 1 <= x^y - 1 2n1<=xy1,求y最小值,扭曲不等式有 n ∗ ( 1 / log ⁡ 2 x ) < = y n * (1 / \log_2x) <= y n(1/log2x)<=y
而shift的值就是 1 / log ⁡ 2 x 1/\log_2x 1/log2x,因此计算结果字符串的长度只需要求mag / shift 并向上取整即可。
这里的 (mag + (shift - 1))/shift 便是mag / shift向上取整的逻辑。
(3)1往左移shift位目的是得到的是待转为进制各位权值的二进制表达。例如:

  • 2进制权值为2 ,mask = radix - 1 = 1 = 二进制 “1”
  • 8进制权值为8 , mask = radix - 1 = 7 = 二进制 “111”
  • 16进制权值为16 , mask = radix - 1 = 15 = 二进制 “1111”

mask为目标进制各位的最大数字, val & mask表示就是val转为目标进制低位的值, digits是个存了[0-z]共36个字符的数组,下标0对应’0’,1对应’1’,15对应’F’。于是以val & mask为下标取得digit字符为val的目标进制的低位字符。 val >>>= shift 相当于value=value>>>shift。从低到高,每获取一位,将位置再往右边移shift (代表待转进制中一位需要的二进制位数) 。 逐字节判断。然后将结果放入buf中。

valueOf方法
public static Integer valueOf(String s) throws NumberFormatException {
     return Integer.valueOf(parseInt(s, 10));
}
public static Integer valueOf(String s, int radix) throws NumberFormatException {
    return Integer.valueOf(parseInt(s,radix));
}

可以看到其内部调用parseInt,前文已经描述过了,不再过多描述

getInteger�()方法

getInteger�较特殊,传入的参数是用来从系统参数内获得value值并进行解析的:

public static Integer getInteger(String nm) {
    return getInteger(nm, null);
}

public static Integer getInteger(String nm, int val) {
    Integer result = getInteger(nm, null);
    return (result == null) ? Integer.valueOf(val) : result;
}

public static Integer getInteger(String nm, Integer val) {
    String v = null;
    try {
        v = System.getProperty(nm);
    } catch (IllegalArgumentException | NullPointerException e) {
    }
    if (v != null) {
        try {
            return Integer.decode(v);
        } catch (NumberFormatException e) {
        }
    }
    return val;
}

简单明了,调用了Integer#decode方法进行解析操作,Integer#decode将会在后面展开。

从数值到字符串转换

toString(int i)方法
public String toString() {
    return toString(value);
}

public static String toString(int i) {
    if (i == Integer.MIN_VALUE)
        return "-2147483648";
    int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
    char[] buf = new char[size];
    getChars(i, size, buf);
    return new String(buf, true);
}

static void getChars(int i, int index, char[] buf) {
    int q, r;
    int charPos = index;
    char sign = 0;

    // 如果数据小于0,则处理正负号
    if (i < 0) {
        sign = '-';
        i = -i;
    }

    // 如果i的4个字节中,前两个字节内也有数据的情况下会> 65536(2^16)
    while (i >= 65536) {
        q = i / 100;
        // 实际的值是:r = i - (q*(64 + 32 + 4)),使用位移代替了
        r = i - ((q << 6) + (q << 5) + (q << 2));
        i = q;
        buf [--charPos] = DigitOnes[r];
        buf [--charPos] = DigitTens[r];
    }

    for (;;) {
        // q = i / 10;
        q = (i * 52429) >>> (16+3);
        // r = i - q * 10;
        r = i - ((q << 3) + (q << 1));  
        buf [--charPos] = digits [r];
        i = q;
        if (i == 0) break;
    }
    if (sign != 0) {
        buf [--charPos] = sign;
    }
}

toString(int i) 方法比较简单,逻辑如下:
(1)stringSize得到数字是多少位
(2)getChars获取数字对应的char数组
(3)使用(2)产生的char数组产生String对象。
其中getChars方法主要做的事情是将某个int型数值放到char数组里面。假定i = 123456,其处理逻辑如下:
(1)判断是否大于65536(2^16,int是32位),如果超过,while循环处理末位2个数字。

  • q=i/100 就是产生了 q = 1234
  • r=i-((q<<6)+(q<<5)+(q<<2)) 实际上就是 r = i - (q * 64 + q32 + q4),得到56

再然后就可以将r放入DigitOnes,DigitTens求得个位和十位上的数字填入数组
(2)第一部分结束之后,相当于剩下了原i内的高位部分。这里的处理逻辑本质也是求余思想,但又用了一些技巧:

  • (i * 52429) >>> (16+3),其中 524290 = 2^19,因此我们等价替换之后,会得到这个式子:

(i * 52429) >>> (16+3) = i * (2^19/10) >>> 19 = i / 10;

  • ((q << 3) + (q << 1))其实等于q*10

然后再通过digits数组获取到对应的字符。可以看到低位处理时它尽量避开了除法,取而代之的是用乘法和右移来实现。另外也可以看到能用移位和加法来实现乘法的地方也尽量不用乘法,这也说明乘法比起它们更加耗时。

toString(int i, int radix)方法
public static String toString(int i, int radix) {
    if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
        radix = 10;

    if (radix == 10) {
        return toString(i);
    }

    char buf[] = new char[33];
    boolean negative = (i < 0);
    int charPos = 32;

    if (!negative) {
        i = -i;
    }

    while (i <= -radix) {
        buf[charPos--] = digits[-(i % radix)];
        i = i / radix;
    }
    buf[charPos] = digits[-i];

    if (negative) {
        buf[--charPos] = '-';
    }

    return new String(buf, charPos, (33 - charPos));
}

此方法带了进制信息的,它会转换成对应进制的字符串。凡是不在2到36进制范围之间的都会被处理成10进制,我们都知道从十进制转成其他进制时就是不断地除于进制数得到余数,然后把余数反过来串起来就是最后结果,所以这里其实也是这样子做的,得到余数后通过digits数组获取到对应的字符,而且这里是用负数的形式来运算的。

decode方法
public static Integer decode(String nm) throws NumberFormatException {
    int radix = 10;
    int index = 0;
    boolean negative = false;
    Integer result;

    if (nm.length() == 0)
        throw new NumberFormatException("Zero length string");
    // 首字母判定,归纳其正负号
    char firstChar = nm.charAt(0);
    if (firstChar == '-') {
        negative = true;
        index++;
    } else if (firstChar == '+')
        index++;

    // 判定是否是16进制,8进制,然后跳index
    if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {
        index += 2;
        radix = 16;
    }
    else if (nm.startsWith("#", index)) {
        index ++;
        radix = 16;
    }
    else if (nm.startsWith("0", index) && nm.length() > 1 + index) {
        index ++;
        radix = 8;
    }

    // 如果经过上面判定之后还会出现正负号则直接报错
    if (nm.startsWith("-", index) || nm.startsWith("+", index))
        throw new NumberFormatException("Sign character in wrong position");

    // 调用字符串转数字的方法
    try {
        result = Integer.valueOf(nm.substring(index), radix);
        result = negative ? Integer.valueOf(-result.intValue()) : result;
    } catch (NumberFormatException e) {
        String constant = negative ? ("-" + nm.substring(index))
                                   : nm.substring(index);
        result = Integer.valueOf(constant, radix);
    }
    return result;
}

decode方法主要作用是解码字符串转成Integer型,输入字符串的可能性有以下几种:

  • 普通数值型字符串:Integer.decode(“11”) = 11;
  • 0开头,Integer.decode(“011”)= 9,因为0开头会被处理成8进制。
  • 0x或者#开头:Integer.decode(“0x11”) = 17 ; Integer.decode(“#11”) = 17。这种形式的字符串将会被转化为十六进制

整体处理逻辑也比较简单,处理逻辑已经在上面的源码中标出。

无符号转换操作

toUnsignedString() 方法
public static String toUnsignedString(int i, int radix) {
    return Long.toUnsignedString(toUnsignedLong(i), radix);
}
public static String toUnsignedString(int i) {
    return Long.toString(toUnsignedLong(i));
}
public static long toUnsignedLong(int x) {
    return ((long) x) & 0xffffffffL;
}

// 选取java.lang.Long
public static String toUnsignedString(long i, int radix) {
    if (i >= 0)
        return toString(i, radix);
    else {
        switch (radix) {
            case 2:
                return toBinaryString(i);

            case 4:
                return toUnsignedString0(i, 2);

            case 8:
                return toOctalString(i);

            case 10:
                long quot = (i >>> 1) / 5;
                long rem = i - quot * 10;
                return toString(quot) + rem;

            case 16:
                return toHexString(i);

            case 32:
                return toUnsignedString0(i, 5);

            default:
                return toUnsignedBigInteger(i).toString(radix);
        }
    }
}

toUnsignedLong方法将int的二进制高位补上零,在long的视角上看就成正数了。toUnsignedString方法则委托Long进行转换。long内对radix进行分类调用内部其他方法,其大致逻辑与Integer相同。

parseUnsignedInt 方法
public static int parseUnsignedInt(String s) throws NumberFormatException {
    return parseUnsignedInt(s, 10);
}

public static int parseUnsignedInt(String s, int radix) throws NumberFormatException {
    if (s == null)  {
        throw new NumberFormatException("null");
    }

    int len = s.length();
    if (len > 0) {
        char firstChar = s.charAt(0);
        if (firstChar == '-') {
            throw new
                NumberFormatException(String.format("Illegal leading minus sign " +
                                                   "on unsigned string %s.", s));
        } else {
            if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
                (radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits
                return parseInt(s, radix);
            } else {
                long ell = Long.parseLong(s, radix);
                if ((ell & 0xffff_ffff_0000_0000L) == 0) {
                    return (int) ell;
                } else {
                    throw new
                        NumberFormatException(String.format("String value %s exceeds " +
                                                            "range of unsigned int.", s));
                }
            }
        }
    } else {
        throw NumberFormatException.forInputString(s);
    }
}

parseUnsignedInt方法主要看第二个,

compareUnsigned 方法
    public static int compareUnsigned(int x, int y) {
        return compare(x + MIN_VALUE, y + MIN_VALUE);
    }
    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

divideUnsigned 方法

无符号除法

    public static int divideUnsigned(int dividend, int divisor) {
        // In lieu of tricky code, for now just use long arithmetic.
        return (int)(toUnsignedLong(dividend) / toUnsignedLong(divisor));
    }

remainderUnsigned 方法

无符号取模方法

public static int remainderUnsigned(int dividend, int divisor) {
    // In lieu of tricky code, for now just use long arithmetic.
    return (int)(toUnsignedLong(dividend) % toUnsignedLong(divisor));
}

基于数字的位操作

bitCount方法
public static int bitCount(int i) {
        i = i - ((i >>> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        return i & 0x3f;
    }

该方法主要用于计算二进制数中1的个数。其中

  • 0x55555555 = 01010101010101010101010101010101
  • 0x33333333 = 00110011001100110011001100110011
  • 0x0f0f0f0f = 00001111000011110000111100001111

它的核心思想就是先每两位一组统计看有多少个1,比如10011111则每两位有1、1、2、2个1,记为01011010,然后再算每四位一组看有多少个1,而01011010则每四位有2、4个1,记为00100100,接着每8位一组就为00000110,接着16位,32位,最终在与0x3f进行与运算,得到的数即为1的个数。

highestOneBit方法
public static int highestOneBit(int i) {
        i |= (i >>  1);
        i |= (i >>  2);
        i |= (i >>  4);
        i |= (i >>  8);
        i |= (i >> 16);
        return i - (i >>> 1);
    }

该方法返回i的二进制中最高位的1,其他全为0的值。比如i=10时,二进制即为1010,最高位的1,其他为0,则是1000。如果i=0,则返回0。如果i为负数则固定返回-2147483648,因为负数的最高位一定是1,即有1000,0000,0000,0000,0000,0000,0000,0000。这一堆移位操作是什么意思?其实也不难理解,将i右移一位再或操作,则最高位1的右边也为1了,接着再右移两位并或操作,则右边1+2=3位都为1了,接着1+2+4=7位都为1,直到1+2+4+8+16=31都为1,最后用i - (i >>> 1)自然得到最终结果。

lowestOneBit方法
public static int lowestOneBit(int i) {
        return i & -i;
    }

与highestOneBit方法对应,lowestOneBit获取最低位1,其他全为0的值。这个操作较简单,先取负数,这个过程需要对正数的i取反码然后再加1,得到的结果和i进行与操作,刚好就是最低位1其他为0的值了。

numberOfLeadingZeros方法
public static int numberOfLeadingZeros(int i) {
    if (i == 0)
        return 32;
    int n = 1;
    if (i >>> 16 == 0) { n += 16; i <<= 16; }
    if (i >>> 24 == 0) { n +=  8; i <<=  8; }
    if (i >>> 28 == 0) { n +=  4; i <<=  4; }
    if (i >>> 30 == 0) { n +=  2; i <<=  2; }
    n -= i >>> 31;
    return n;
}

表达无符号右移,高位填充0,例如 1101 >>> 1 = 0110
<< 表达左移,低位填充0
n-=i>>>31 的处理逻辑是 n = n - (i>>>31)

该方法返回i的二进制从头开始有多少个0。i为0的话则有32个0。这里处理其实是体现了二分查找思想的,先看高16位是否为0,是的话则至少有16个0,否则左移16位继续往下判断,接着右移24位看是不是为0,是的话则至少有16+8=24个0,直到最后得到结果。

numberOfTrailingZeros方法
public static int numberOfTrailingZeros(int i) {
        int y;
        if (i == 0) return 32;
        int n = 31;
        y = i <<16; if (y != 0) { n = n -16; i = y; }
        y = i << 8; if (y != 0) { n = n - 8; i = y; }
        y = i << 4; if (y != 0) { n = n - 4; i = y; }
        y = i << 2; if (y != 0) { n = n - 2; i = y; }
        return n - ((i << 1) >>> 31);
    }

与前面的numberOfLeadingZeros方法对应,该方法返回i的二进制从尾开始有多少个0。它的思想和前面的numberOfLeadingZeros类似,也是基于二分查找思想,详细步骤不再赘述。

reverse方法
public static int reverse(int i) {
        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;
    }

该方法即是将i进行反转,反转就是第1位与第32位对调,第二位与第31位对调,以此类推。它的核心思想是先将相邻两位进行对换,比如10100111对换01011011,接着再将相邻四位进行对换,对换后为10101101,接着将相邻八位进行对换,最后把32位中中间的16位对换,然后最高8位再和最低8位对换。

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

原理同reverse方法

signum方法
public static int signum(int i) {
        // HD, Section 2-7
        return (i >> 31) | (-i >>> 31);
    }

返回指定 int 值的符号函数,逻辑简单。

rotateRight
public static int rotateRight(int i, int distance) {
    return (i >>> distance) | (i << -distance);
}

此方法用于返回通过将给定参数(值)的二进制2的补码符号向右旋转给定位数而生成的值。

rotateLeft
public static int rotateLeft(int i, int distance) {
    return (i << distance) | (i >>> -distance);
}

此方法用于返回通过将给定参数(值)的二进制2的补码符号向左旋转给定位数而生成的值。

纯数值操作

数值操作提供了 sum, min, max操作,都比较简单,在此不再过多描述。

public static int sum(int a, int b) {
    return a + b;
}

public static int min(int a, int b) {
    return Math.min(a, b);
}

public static int max(int a, int b) {
    return Math.max(a, b);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值