Java源碼閱讀筆記之Integer

Integer的基本實現
Integer的使用
Integer封裝的操做

Integer的基本實現

基本描述:
Integer是對原生基本類型int的封裝,其定義value來存儲值和一些用於描述int的信息java

int value;//int
 int SIZE = 32;//1位正負標識+31位數據
 int BYTES = SIZE / Byte.SIZE;//所佔字節
 int   MIN_VALUE = 0x80000000;//最小值,32個1
 int   MAX_VALUE = 0x7fffffff;//最大值,0+31個1

構造函數:
容許經過String和int入參來爲value賦值,可是兩個構造函數都已棄用git

經過註釋能夠看到,推薦經過valueOf()的方法來返回一個Integer數組

/**
    * @deprecated
     * It is rarely appropriate to use this constructor. The static factory
     * {@link #valueOf(int)} is generally a better choice, as it is
     * likely to yield significantly better space and time performance.
     */
  @Deprecated(since="9")
  public Integer(int value) {
     this.value = value;
   }

    /**
     * @deprecated
     * It is rarely appropriate to use this constructor.
     * Use {@link #parseInt(String)} to convert a string to a
     * {@code int} primitive, or use {@link #valueOf(String)}
     * to convert a string to an {@code Integer} object.
     */
  @Deprecated(since="9")
  public Integer(String s) throws NumberFormatException {
     this.value = parseInt(s, 10);
  }

使用推薦的方法獲取Integer實例和構造方法有何不一樣?app

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

//----------------------String入參------------------
    public static Integer valueOf(String s) throws NumberFormatException {
        return Integer.valueOf(parseInt(s, 10));
    }

    //radix表示進制,取值範圍爲[2, 36]
    public static Integer valueOf(String s, int radix) throws NumberFormatException {
        return Integer.valueOf(parseInt(s,radix));
    }
  • int入參

若是入參中的int在IntegerCache內部類的Integer cache[]中存在則返回數組中的Integer不然經過構造函數建立(棄用的那個)jvm

  • String入參

經過parseInt(s,radix)方法解析字符串,返回int值
radix參數表示字符串轉換的int值的進制,其取值範圍爲[2,36]函數

解析IntegerCache和parseInt的實現this

IntegerCache
//The cache is initialized on first usage.
    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;
            //The size of the cache may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
            String integerCacheHighPropValue =
                VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    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);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

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

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

IntegerCache是一個私有靜態內部類該類內部定義了一個數組Integer cache[],數組內的數據由-128起始,默認至127爲止(byte的範圍)spa

該數組的最大值可經過在jvm中設置
-XX:AutoBoxCacheMax=<size>來設置其大小code

數組cache[128]爲0,valueof(int)參數的值符合這個範圍都會直接從數組中返回Integerorm

有意思的是valueof(int)是@HotSpotIntrinsicCandidate的,關於它的描述是這樣的:

JDK的源碼中,被@HotSpotIntrinsicCandidate標註的方法,在HotSpot中都有一套高效的實現,該高效實現基於CPU指令,運行時,HotSpot維護的高效實現會替代JDK的源碼實現,從而得到更高的效率。

估計這就是推薦使用的主要緣由吧!

parseInt
public static int parseInt(String s, int radix)
                throws NumberFormatException
    {
      ...

        boolean negative = false;//正負標識
        int i = 0, len = s.length();
        int limit = -Integer.MAX_VALUE;

        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++;
            }
            //轉化邏輯
            int multmin = limit / radix;
            int result = 0;
            while (i < len) {
                // Accumulating negatively avoids surprises near MAX_VALUE
                int digit = Character.digit(s.charAt(i++), radix);
                if (digit < 0 || result < multmin) {
                    throw NumberFormatException.forInputString(s);
                }
                result *= radix;
                if (result < limit + digit) {
                    throw NumberFormatException.forInputString(s);
                }
                result -= digit;
            }
            return negative ? result : -result;
        } else {
            throw NumberFormatException.forInputString(s);
        }
    }

    static final 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'
    };

字符串轉化爲int的關鍵在於digits數組,以16進製爲例,用0...9,a...f表示0到15,滿16纔會進1。也就是超過10進制之後,大於10的數要使用a開始的字母表示,可是字母只有26個,進制又必須從2開始,故進制的取值範圍也就定義爲[2, 36]

故入參的字符串s也必須符合digits數組中的元素以及額外的只可能存在第一位"+"或者"-"

parseInt的轉化邏輯爲:
在每次循環中

  • 取出digit,肯定進制後轉化的int數
  • 經過result *= radix;把上一次循環的數據進一位
  • 經過result -= digit;把當前的數據加入result

而後返回結果,經過:
return negative ? result : -result;

Integer的使用

int a = 5;
   Integer w = 6;
   Integer test = Integer.valueOf(w);
   int testP = Integer.valueOf(a);

轉化成對應的字節碼,則

  • int a = 5

0: iconst_5
1: istore_1
直接將天然數壓棧

  • Integer w = 6
    2: bipush 6
    4: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    7: astore_2

調用Integer的靜態方法valueof(6)獲得Integer實例

  • Integer test = Integer.valueOf(w)
    8: aload_2
    9: invokevirtual #3 // Method java/lang/Integer.intValue:()I
    12: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    15: astore_3

獲取操做數棧中w的引用,調用intValue返回int值,再經過valueof獲取Integer實例

  • int testP = Integer.valueOf(a)
    16: iload_1
    17: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
    20: invokevirtual #3 // Method java/lang/Integer.intValue:()I

獲取操做數棧中的a,調用valueof獲取Integer實例,再經過intValue返回int值

由此可知,對於基本類型的封裝類,編譯器會自動調用其一些方法來實現用戶操做的簡化!

Integer封裝的操做

Object虛函數的實現
父類Number的虛函數實現
字節操做

Object虛函數的實現

public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

    public static int hashCode(int value) {
        return value;
    }

    public static String toString(int i) {
        int size = stringSize(i);
        if (COMPACT_STRINGS) {
            byte[] buf = new byte[size];
            getChars(i, size, buf);
            return new String(buf, LATIN1);
        } else {
            byte[] buf = new byte[size * 2];
            StringUTF16.getChars(i, size, buf);
            return new String(buf, UTF16);
        }
    }
  • equals

經過Integer的intValue獲取入參的Integer封裝的int值並與value進行==尋址判斷

  • hashCode

hashCode返回的就是一個int值,故直接使用value自己

  • toString

使用char數組作中轉,經過String實例化一個String實例
根據是否開啓壓縮機制判斷使用的是LATIN1仍是UTF16

父類Number的虛函數實現

public byte byteValue() {
        return (byte)value;
    }

    public double doubleValue() {
        return (double)value;
    }

    public float floatValue() {
        return (float)value;
    }

    public int intValue() {
        return value;
    }

    public long longValue() {
        return (long)value;
    }

    public short shortValue() {
        return (short)value;
    }

只是對value進行強轉

字節操做

計算int二進制形式左(右)側有幾個0,遇到1就中止計數
計算int二進制形式1的數量
左(右)移二進制形式
按位(字節)置換

計算int二進制形式左(右)側有幾個0,遇到1就中止計數

//左側
    public static int numberOfLeadingZeros(int i) {
        // HD, Count leading 0's
        if (i <= 0)
            return i == 0 ? 32 : 0;
        int n = 31;
        if (i >= 1 << 16) { n -= 16; i >>>= 16; }
        if (i >= 1 <<  8) { n -=  8; i >>>=  8; }
        if (i >= 1 <<  4) { n -=  4; i >>>=  4; }
        if (i >= 1 <<  2) { n -=  2; i >>>=  2; }
        return n - (i >>> 1);
    }

    //右側
    public static int numberOfTrailingZeros(int i) {
        // HD, Figure 5-14
        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

1 負數1標識,左側無0,0全爲0,直接返回32(int爲32位)
2 經過1 << 16判斷,判斷條件爲是否比它大,左邊16位是否全爲0,決定接下來操做左或右半邊
3 再經過i << 8,4,2,1折半再折半計算出不爲0的數字的位置,從而得出0的數量

  • 右側:numberOfTrailingZeros

經過i <<16,不爲0則右邊有1,再i << 8,4,2,1,判斷出右邊數起的第一個1,從而計算出0的數量

計算int二進制形式1的數量

public static int bitCount(int i) {
        // HD, Figure 5-2
        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;
    }
  • 0x5 = 0101,經過作&運算記錄雙數位的數據狀況

0x3 = 0011,經過作&運算記錄後兩位的數據狀況
0x0f = 0000 1111,經過作&運算記錄後四位的數據狀況

  • 1 int的二進制形式的可能有 00,01,10,11

先作>>>右移一位再與01作&運算,記錄了兩位二進制左邊數字的1的數量,再用原來的二進制數減去記錄的值
如11:11-01=10(11有兩個1)
2 通過第一步計算,記錄了以兩位數爲單位的1的數量
把第一步的結果與0011作&運算獲得四位二進制結果的後兩位計算,0011再與四位二進制結果>>>右移兩位計算前兩位的結果,再把其相加獲得四位數中1的數量
如1011
1011 - 0101 = 0110
0001 + 0010 = 0011(1011有三個1)
3 i + (i >>> 4),i + (i >>> 8),i + (i >>> 16)分別把獲得的上一步計算的結果整合計算
計算完成後記錄結果的有效位數只有右邊八位,32位數一共最多32個1,因此實際的有效位數只有右邊6位

左(右)移二進制形式

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獲得移動消逝的數據,並將其放在補0的位置

  • -distance表示移動-distance負數的表現形式int截取5位,long截取6位,如-1爲32個1,截取5位爲1 1111,爲31,也就是不算位移,移動的「路程」是32,正好把移出的數據再補回補0的地方

按位(字節)置換

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

    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;

        return reverseBytes(i);
    }
  • 按字節置換:reverseBytes

i << 24與i >>> 24作 | 運算獲得最左右兩邊的置換
0xff00二進制形式爲1111 1111 0000 0000
正好用來處理中間左八位和右八位的交換,主要是&和移動的前後來實現不一樣的位的清零

  • 按位置換:reverse

1 使用01來記錄兩位二進制中的一位,再經過移動記錄另外一位,作 | 運算的會把兩位的二進制數交換位置
2 經過0011來交換四位中的前兩位和後兩位
3 經過0000 1111來交換前四位和後四位
4 經過前三步實現交換每8位的循序,再經過按字節置換交換所有的順序

後話

Integer中還有關於

static final byte[] 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',
        } ;

    static final byte[] 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',
        } ;

這兩個數組的應用和字符和byte之間轉換的精彩實現,有時間會記錄。

相關文章
相關標籤/搜索