一起學(xué)JDK源碼 -- Integer類

Integer類為java基本類型int的包裝類,除了前面提到的Byte類,Short類中的大部分方法,Integer類中還提供了很多處理int類型的方法,接下來就讓我們一起看看吧。

基礎(chǔ)知識:

1.Java移位運算,移位運算也是java中位運算的一部分主要有以下三種:
前提:
1).移位是在將對應(yīng)的數(shù)字轉(zhuǎn)換為2進制后進行的,JAVA中用補碼表示二進制數(shù).
2).移位后總長度不變,比如int類型為32位,移動后還需補齊為32位,其它也類似
3).左邊為低位,右邊為高位
左移( << ):將運算符左邊的對象,向左移動運算符右邊指定的位數(shù),并且在低位補零。向左移n 位,就相當于乘上2 的n 次方
右移( >> ):將運算符左邊的運算對象,向右移動運算符右邊指定的位數(shù)。如果是正數(shù),在高位補零,如果是負數(shù),則在高位補1
無符號右移( >>> ):將運算符左邊的對象向右移動運算符右邊指定的位數(shù),并且在高位補0
注:
1).對于正數(shù)來說,右移或無稱號右移n 位,就相當于除上2 的n 次方。
2).對于int類型(長度為32位)移位,左移(<<)n位,相當于移動 n % 32 位,如: 1 << 33 實際上是 1 << 1 也就是2,其它類型移動超過自身類型長度的也應(yīng)該是類似,有興趣的可以自行研究下。
3).java中沒有無符號左移,原因很簡單,左移補的是低位,java中高位的第一位表示符號位。

前言:

從本文開始,對于比較簡單的屬性和方法,或是在之前文章中講過的文章中將不再列出,有興趣的同學(xué)可以看下之前的文章,或是留言筆者將根據(jù)大家的需求考慮是否后續(xù)在文章中添加。

幾個數(shù)組屬性:

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 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 int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                      99999999, 999999999, Integer.MAX_VALUE };

digits數(shù)組用于表示所有可能出現(xiàn)的字符,因為int支持從2進制到36進制,所以這里需要有36個字符才能表示所有不同進制的數(shù)字
DigitTens和DigitOnes兩個數(shù)組也很好理解,它們主要用于獲取0到99之間某個數(shù)的十位和個位,比如36,通過DigitTens數(shù)組直接取出來十位為3,而通過DigitOnes數(shù)組取出來個位為6。
sizeTable數(shù)組主要用在判斷一個int型數(shù)字對應(yīng)字符串的長度。避免了使用除法或求余等操作,以提高效率。

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

該方法的作用就是將int類型的數(shù)字,轉(zhuǎn)換為指定進制的數(shù)的字符串形式。
第一步判斷:int類型支持的進制數(shù)為2(Character.MIN_RADIX)到36(Character.MAX_RADIX),如果不是這個范圍就按10進制來處理。
第二步:如果是10進制,調(diào)用toString(i)(這個方法我們后面再講),如果不是10進制,繼續(xù)往下走。
第三步:接著它創(chuàng)建了一個長度為33的char類型的數(shù)組,誒,為什么這里長度為33,int類型的最大長度為32啊,看到后面你就知道了,還要給 '-'留一個位置,原來這個方法的轉(zhuǎn)換并不是真正意義上的轉(zhuǎn)換,都是按照正數(shù)來轉(zhuǎn)換,如果是負數(shù)就在正數(shù)轉(zhuǎn)換的結(jié)果上加'-'。
第四步:創(chuàng)建了兩個局部變量negative和charPos其中negative是個標識,用來標識目標數(shù)是正數(shù)還是負數(shù)。charPos是用來指定轉(zhuǎn)換后的數(shù)存儲在緩沖數(shù)組中的位置。
第五步:轉(zhuǎn)換,這里就是按照10進制的數(shù)轉(zhuǎn)換為其它進制數(shù)的轉(zhuǎn)換方法進行的。如果不清楚的同學(xué)可以看下另一篇文章Java中的進制轉(zhuǎn)換
第六步:創(chuàng)建一個字符串對象返回。

toString(int i):

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

這個方法用來將10進制的數(shù)轉(zhuǎn)換為10進制的字符串形式。
第一步:判斷如果是int類型的最小值,就直接返回一個固定的值,無需計算。
第二步:計算數(shù)字的長度,用到的stringSize方法,如下:

static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
}

可以看到它使用了sizeTable這個數(shù)組,巧妙的避免了除法和求余,以高效的求得對應(yīng)字符串長度。方法實現(xiàn)很簡單,但是思想我們可以借鑒。
第三步:創(chuàng)建了一個數(shù)組,然后調(diào)用getChars方法填充數(shù)組中的內(nèi)容。getChars方法實現(xiàn)如下:

static void getChars(int i, int index, char[] buf) {
        int q, r;
        int charPos = index;
        char sign = 0;
        if (i < 0) {
            sign = '-';
            i = -i;
        }
        // Generate two digits per iteration
        while (i >= 65536) {
            q = i / 100;
        // really: r = i - (q * 100);
            r = i - ((q << 6) + (q << 5) + (q << 2));
            i = q;
            buf [--charPos] = DigitOnes[r];
            buf [--charPos] = DigitTens[r];
        }
        // Fall thru to fast mode for smaller numbers
        // assert(i <= 65536, i);
        for (;;) {
            q = (i * 52429) >>> (16+3);
            r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
            buf [--charPos] = digits [r];
            i = q;
            if (i == 0) break;
        }
        if (sign != 0) {
            buf [--charPos] = sign;
        }
}

這個方法的作用就是將一個int類型的數(shù)按順序放到char數(shù)組中。不過這里使用了很多的技巧。它把int的高位的兩個字節(jié)和低位的兩個字節(jié)分開處理,while (i >= 65536)就是處理高位的兩個字節(jié)`。每次處理兩位數(shù),使用了一個技巧((q << 6) + (q << 5) + (q << 2)),就是 q * (2^6 +2^5 + 2^2) = q * 100。DigitTens和DigitOnes用來取十位和個位。后面就是對低位的兩個數(shù)進行處理了,其本質(zhì)就是求余,也用了一些技巧,(i * 52429) >>> (16+3)其實約等于i/10,((q << 3) + (q << 1))其實等于q*10,最后通過digits數(shù)組獲取到對應(yīng)的字符。這里面使用乘法和移位運算代替除法和取余,用移位運算和加法代替乘法,可見在運算效率上加法和移位高于乘法,乘法高于除法。
第四步:根據(jù)填充好的char數(shù)組創(chuàng)建字符串對象返回
注:
1.這個方法也就是toString(int i, int radix)方法中對于10進制的處理。可以看到對于10進制的轉(zhuǎn)化,可是花了大功夫在優(yōu)化上,為了避免使用除法,應(yīng)用各種移位操作來進行,即使在不得不使用除法的地方也盡可能用除以100而不是除以10來減少除法次數(shù)。
2.這里使用getChars去填充char數(shù)組,顯然寫這段代碼的之前是一個寫C語言這種面向過程的人。對于java這種面向?qū)ο蟮恼Z言來說,這里我們應(yīng)該用 char[] buf = getChars(i, size);這種方式,以后大家寫java代碼我也推薦大家使用這種方式。什么沒看明白,自己去百度下面向過程和面向?qū)ο蠓矫娴闹R吧。

int類型轉(zhuǎn)換為其它進制的字符串形式:

//轉(zhuǎn)換為16進制
public static String toHexString(int i) {
        return toUnsignedString0(i, 4);
}
//轉(zhuǎn)換為8進制
public static String toOctalString(int i) {
        return toUnsignedString0(i, 3);
}
//轉(zhuǎn)換為2進制
public static String toBinaryString(int i) {
        return toUnsignedString0(i, 1);
}

可以看到這幾個轉(zhuǎn)換的方法都調(diào)用了同一個方法toUnsignedString0只是傳的參數(shù)不同,toUnsignedString0方法如下:

private static String toUnsignedString0(int val, int shift) {
        // assert shift > 0 && shift <=5 : "Illegal shift value";
        int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
        int chars = Math.max(((mag + (shift - 1)) / shift), 1);
        char[] buf = new char[chars];
        formatUnsignedInt(val, shift, buf, 0, chars);
        // Use special constructor which takes over "buf".
        return new String(buf, true);
}

第一步:獲取目標值value的二進制形式有效位長度,Integer.numberOfLeadingZeros(val)這個方法是計算value的二進制高位共有多少位0,后面我們再講這個方法。
第二步:獲取緩沖數(shù)組的大小
第三步:調(diào)用formatUnsignedInt方法,這個方法才是核心,原因如下:

static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
        int charPos = len;
        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;
}

這里就是將10進制數(shù)轉(zhuǎn)換為其它進制數(shù)的代碼體現(xiàn)了,其中 1 << shift相當于2^shift,radix - 1如果轉(zhuǎn)換為16進制就是15如果是8進制就是7,這里是為了后面做&的計算,你想一下如果轉(zhuǎn)換為16進制,一個數(shù)&上15(1111),就是取得這個數(shù)字的低4位,這樣4位4位的計算不就轉(zhuǎn)換為這個數(shù)的16進制了嗎,轉(zhuǎn)換為8進制同理。后面的val&mask 就相當于 val%(mask+1)也就是進制轉(zhuǎn)換的過程了。
第四步:根據(jù)buf數(shù)組,創(chuàng)建String對象后返回。

parseInt:

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') { // Possible leading "+" or "-"
                if (firstChar == '-') {
                    negative = true;
                    limit = Integer.MIN_VALUE;
                } else if (firstChar != '+')
                    throw NumberFormatException.forInputString(s);
                if (len == 1) // Cannot have lone "+" or "-"
                    throw NumberFormatException.forInputString(s);
                i++;
            }
            multmin = limit / radix;
            while (i < len) {
                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有兩個方法,作用是將指定進制的數(shù)的字符串形式轉(zhuǎn)換為int類型??瓷厦孢@個就行。
第一步:參數(shù)校驗。字符串為null或是radix不在可轉(zhuǎn)換的進制范圍內(nèi)就拋出異常。
第二步:核心處理邏輯是字符串轉(zhuǎn)換數(shù)字,其它進制轉(zhuǎn)成十進制,如521為8進制,則結(jié)果為5* 8^2 + 28^1 + 18^0.上面的轉(zhuǎn)換方法也差不多是根據(jù)此方法,只是稍微轉(zhuǎn)變了思路( (5 * 8+2)*8)+1結(jié)果都是一樣的。規(guī)律就是從左到右遍歷字符串的每個字符,然后乘以進制數(shù),再加上下一個字符,接著再乘以進制數(shù),再加上下個字符,不斷重復(fù),直到最后一個字符。除此之外另外一個不同就是上面的轉(zhuǎn)換不使用加法來做,全都轉(zhuǎn)成負數(shù)來運算,其實可以看成是等價了。因為負數(shù)Integer.MIN_VALUE變化為正數(shù)時會導(dǎo)致數(shù)值溢出,所以全部都用負數(shù)來運算。
第三步:返回正確的結(jié)果如果是負數(shù)就添加負號。

無符號轉(zhuǎn)換:

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

這兩個方法就是將目標數(shù)轉(zhuǎn)換為無符號表示的long或是字符串,無符號意思就是將二進制中的高位的第一位不當作符號位來看待。其中toUnsignedString使用了Long.toUnsignedString方法,一直跟蹤源碼可以發(fā)現(xiàn)實現(xiàn)方式與Integer中的實現(xiàn)方式相同,這里就不再重復(fù)了,不明白的同學(xué)可以看下Integer中的toString方法和toUnsignedString0方法。

parseUnsignedInt:

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

將目標字符串數(shù)字轉(zhuǎn)換為指定進制的無符號整型。
第一步:獲取第一個字符,判斷是否為'-',如果是'-'就拋出異常。
第二步:判斷長度小于5或是小于9的10進制,使用parseInt方法長度小于5是因為int類型的最大數(shù)轉(zhuǎn)換為最大進制36進制是6位。parseInt方法在上面已經(jīng)講過了。
第三步:如果超出第二步判斷的范圍,就要使用long來轉(zhuǎn)換了,否則可能溢出。 if ((ell & 0xffff_ffff_0000_0000L) == 0) 若未超過int無符號數(shù)支持的范圍,即數(shù)字高八位為0,則返回int值,否則拋出異常,數(shù)字超過int可表示的范圍。

IntegerCache:

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() {}
}

可以看出Integer類也有如同Byte和Short類類似的緩存池機制,而不同點在于Integer的緩存池可以改變上限的大小,通過 sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");來獲取上限,這個參數(shù)可以在JVM中配置。但是在設(shè)置的時候這個值是要大于或等于127小于Integer.MAX_VALUE。如果小于127以127為上限。注意。下限是-128這個是不可以改變的。

getInteger:

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

這個方法是從系統(tǒng)屬性中查找數(shù)據(jù)然后轉(zhuǎn)換為對應(yīng)的Integer對象,如果系統(tǒng)中不存在待查找的屬性,則返回null。單從方法名上看Integer.getInteger與Integer.valueOf(由于valueOf方法在之前的Byte類Short類中已經(jīng)講過了,都是相似的,這里不贅述)方法功能是一樣的,但實際則不然,我們在使用的時候也要小心一點。

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);
        // Handle sign, if present
        if (firstChar == '-') {
            negative = true;
            index++;
        } else if (firstChar == '+')
            index++;
        // Handle radix specifier, if present
        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) {
            // If number is Integer.MIN_VALUE, we'll end up here. The next line
            // handles this case, and causes any genuine format error to be
            // rethrown.
            String constant = negative ? ("-" + nm.substring(index))
                                       : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }
        return result;
}

這個方法是用來解析2進制,8進制,10進制16進制表示的字符串的。我們用數(shù)字前面加0表示8進制如056,數(shù)字前面加0x或是#,如0x1234或是#1234,表示16進制,默認數(shù)字為10進制。注意這個方法也不是太智能,如你傳100這個字符串,他就不知道是10進制還是2進制,默認當10進制來轉(zhuǎn)換了。又如你給定字符串 0000 0000 0000 0100 ,你想讓他按2進制來解析,但實際上他按8進制來解析。所以使用的時候如果有這種有歧義的字符串,要小心一些。

無符號比較:

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

無符號就是二進制的最高位符號位不當作符號位來,比較,實現(xiàn)過程也很簡單。那么這個函數(shù)用來比較兩個數(shù)-1是最大的,因為-1的所有位都是1,當然最大。

divideUnsigned:

public static int divideUnsigned(int dividend, int divisor) {
        return (int)(toUnsignedLong(dividend) / toUnsignedLong(divisor));
}

divide意為除的意思,這個方法就是將兩個數(shù)轉(zhuǎn)換為無符號的數(shù),然后再相除。

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

將兩個數(shù)轉(zhuǎn)換為無符號的數(shù),然后再計算余數(shù),可以看到源碼的注釋 // In lieu of tricky code, for now just use long arithmetic.意思就是因為這個情況很棘手,所以現(xiàn)在暫時用long型數(shù)據(jù),具體待我們看到Long類的源碼時再研究

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

乍一看這個方法是干什么的,不明白。按照位運算|和移位運算一步一步的跟下來,你就會發(fā)現(xiàn),將目標數(shù)轉(zhuǎn)換為二進制后。保留這個數(shù)的最高位1,即從右邊開始數(shù)第一個1保留。然后其它位全變?yōu)?后的結(jié)果。如10 ,二進制為1010,保留最高位1,其它位全變0就是1000,結(jié)果就為8.

lowestOneBit:

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

同highestOneBit類似,從左邊開始數(shù)第1位1保留,其它全變0后的結(jié)果。

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

這個函數(shù)的作用就是,取一個int類型數(shù)的二進制左邊共有多少位0.如10的二進制為1010,int共有32位,那么左邊就有28個0,即numberOfLeadingZeros(10)結(jié)果為28。這個方法作用很簡單,但實現(xiàn)這個方法用的一個思想?yún)s是很重要,值得我們一學(xué)。有興趣的同學(xué)可以看下我的別一篇文章從JDK源碼看二分思想。

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類似,這個方法的作用就是取一個int類型的二進制右邊共有多少位0。如10的二進制為1010.右邊共有1個0.numberOfTrailingZeros(10)結(jié)果就為1。

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

這個方法的作用是,將一個int類型的數(shù)轉(zhuǎn)換為二進制后,數(shù)里面有多少個1。如10,二進制為1010,有兩個1,那么bitCount(10)結(jié)果就為2。其核心思想是使用二分法,兩兩一組相加,之后四個四個一組相加,接著八個八個,最后就得到各位之和了。 第一行是計算每兩位中的 1 的個數(shù) , 并且用該對應(yīng)的兩位來存儲這個個數(shù) , 如 : 01101100 轉(zhuǎn)換后為 01011000 , 即先把前者每兩位分段 01 10 11 00 , 分別有 1 1 2 0 個 1, 用兩位二進制數(shù)表示為 01 01 10 00, 合起來為 01011000。 第二行是計算每四位中的 1 的個數(shù) , 并且用該對應(yīng)的四位來存儲這個個數(shù) 。如 : 01101100 經(jīng)過第一行計算后得 01011000 , 然后把 01011000 每四位分段成 0101 1000 , 段內(nèi)移位相加 : 前段 01+01 =10 , 后段 10+00=10, 分別用四位二進制數(shù)表示為 0010 0010, 合起來為 00100010 . 下面的各行以此類推 , 分別計算每 8 位 ,16 位 ,32 位中的 1 的個數(shù) 。

rotateLeft:

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

(i << distance) 先把尾巴那幾位空出來成0,然后(i >>> -distance)獲得前面的那幾位,然后按位或運算,就旋轉(zhuǎn)了,即循環(huán)左移。

rotateRight:

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

跟前面的rotateLeft一樣,先把位置空出來,然后取得對應(yīng)的二進制位,按位或運算,就成了。

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

將目標數(shù)轉(zhuǎn)換為32位二進制數(shù)據(jù)后,高位和低位對應(yīng)位置數(shù)據(jù)互換。如10轉(zhuǎn)換為2進制后為00000000 000000000 00000000 0001010則轉(zhuǎn)換后數(shù)據(jù)為01010000 00000000 00000000 00000000怎么樣很好理解吧。其實Integer中的很多操作都是轉(zhuǎn)換為二進制后,再處理的,光看代碼很復(fù)雜。把數(shù)據(jù)轉(zhuǎn)換成二進制,然后再對照代碼看就很簡單了。

signum:

public static int signum(int i) {
        return (i >> 31) | (-i >>> 31);
}

這個方法我還沒研究明白是干什么用的,i >> 31這個如果i為正數(shù)運算后全變?yōu)?,如果是負數(shù)運算后全變?yōu)?(指的是二進制位),(-i >>>31) 如果i為正數(shù),運算后為1,如果i為負數(shù)運算后為0。兩個數(shù)做|運算后,如果i為正則結(jié)果為1,如果為負數(shù)結(jié)果為-1,如果是0則結(jié)果為0,估計是運來判斷一個數(shù)是正數(shù)負數(shù)還是0吧。如果是這樣的話直接跟0做比較不就好了,寫這段代碼干什么呢,搞不明白。

reverseBytes:

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

這個方法同Byte類的reverseBytes方法有點類似。但這里是高8位與低8位互換,中間的兩個8位互換之后的結(jié)果。轉(zhuǎn)換成二進制后就一目了然了,我就不舉例了。

其它方法:

//求兩個數(shù)之和
public static int sum(int a, int b) {
   return a + b;
}
//取兩個數(shù)中較大的數(shù)
public static int max(int a, int b) {
    return Math.max(a, b);
}
//取兩個數(shù)中較小的數(shù)
public static int min(int a, int b) {
    return Math.min(a, b);
}

這幾個方法挺簡單的,大家一看就明白了。

注:

  • Integer類中的很多方法都是操作二進制數(shù)的。光看代碼可能一頭霧水,轉(zhuǎn)換為二進制后,再輔助代碼就直觀多了。
  • 這個類中涉及了很多的位運算,如果對位運算不清楚的可以看下一起學(xué)JDK源碼 -- Byte類和本章的基礎(chǔ)知識部分。
  • 這個類中有一些方法如signum、reverseBytes等操作二進制數(shù)的思想到是很精彩。但實際不知道有什么用。不知道是作者炫耀它的思想,還是我太菜沒看出來他的意圖。

查看所有目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line),也就是一...
    悟名先生閱讀 4,555評論 0 13
  • 一、Java 簡介 Java是由Sun Microsystems公司于1995年5月推出的Java面向?qū)ο蟪绦蛟O(shè)計...
    子非魚_t_閱讀 4,562評論 1 44
  • 1 關(guān)鍵字 1.1 關(guān)鍵字的概述 Java的關(guān)鍵字對java的編譯器有特殊的意義,他們用來表示一種數(shù)據(jù)類型,或...
    哈哈哎呦喂閱讀 781評論 0 0
  • 在知識變現(xiàn)如此多元化的今天,掌握學(xué)習(xí)方法,能讓你在人群中快速脫穎而出。身為普通人的我們,學(xué)習(xí)就成了一門非常重要的技...
    陶壹閱讀 153評論 0 0
  • 1人最不愿意被別人救贖。我想在以后更多傾聽別人。之前會想著自己學(xué)了挺多,給別人建議。所幸,還有點覺知。 2.不需要...
    劉姥姥2017閱讀 97評論 0 0

友情鏈接更多精彩內(nèi)容