首页 > Java开发 > Java整数类型

Java整数类型

这一篇主要介绍Java中经常使用的整数类型 - Integer

首先来说一下位运算,位运算应用是非常广的。无论是名企的笔试、面试,还是Java的源代码,这种应用随处可见。关于位运算我不想说太多,可以给大家推荐一篇非常不错的博文,地址如下:

http://blog.csdn.net/morewindows/article/details/7354571

同样,在Integer类的源代码中,Java的设计师们为了提高效率使用了大量的位运算,首先来看一个简单的进制转换源代码:

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1.  //无符号整数 16进制,向右移动4位
  2.  public static String toHexString(int i) {
  3.      return toUnsignedString(i, 4);
  4.  }
  5.  //无符号整数 8进制,向右移动3位
  6.  public static String toOctalString(int i) {
  7.      return toUnsignedString(i, 3);
  8.  }
  9. //无符号整数  2进制,向右移动2位
  10.  public static String toBinaryString(int i) {
  11.      return toUnsignedString(i, 1);
  12.  }
  13.  // 对无符号整数进行转换
  14.  private static String toUnsignedString(int i, int shift) {
  15.      char[] buf = new char[32];
  16.      int charPos = 32;
  17.      int radix = 1 << shift;
  18.      int mask = radix - 1;
  19.      do {
  20.          buf[--charPos] = digits[i & mask];
  21.          i >>>= shift;//i=i>>>shift
  22.      } while (i != 0);
  23.      return new String(buf, charPos, (32 - charPos));
  24.  }

可以看一下对于10进制无符号整数的进制转换。由于缺少边界等的检查,所以toUnsignedString()方法并没有公开,而是提供了多个常用进制转换的方法。最后返回了转换后的字符串表示形式。至于源码中为什么要定义一个32的字符数组后面将会分析到。

 

我们知道,Java中的int数值类型占用4个字节,有时候需要对表示一个int数值的4个字节做一些特殊的处理,如字节反转、位反转等,源代码如下:

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. // 将4个字节的顺序进行逆转,并不是对位进行逆转
  2.    public static int reverseBytes(int i) {
  3.        return ((i >>> 24)           ) |
  4.               ((i >>   8) &   0xFF00) |
  5.               ((i <<   8) & 0xFF0000) |
  6.               ((i << 24));
  7.    }
  8.    // 对位进行逆转
  9.    public static int reverse(int i) {
  10.        // HD, Figure 7-1
  11.        i = (i & 0x55555555) << 1 | (i >>> 1) & 0x55555555;
  12.        i = (i & 0x33333333) << 2 | (i >>> 2) & 0x33333333;
  13.        i = (i & 0x0f0f0f0f) << 4 | (i >>> 4) & 0x0f0f0f0f;
  14.        i = (i << 24) | ((i & 0xff00) << 8) |
  15.            ((i >>> 8) & 0xff00) | (i >>> 24);
  16.        return i;
  17.    }

大家可以好好研究一下如上的代码是怎么实现他们的功能的。继续看下面的两个方法:

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public static int highestOneBit(int i) {
  2.      // HD, Figure 3-1
  3.      i |= (i >>  1);
  4.      i |= (i >>  2);
  5.      i |= (i >>  4);
  6.      i |= (i >>  8);
  7.      i |= (i >> 16);
  8.      return i - (i >>> 1);
  9.  }
  10.  public static int lowestOneBit(int i) {
  11.      return i & -i;
  12.  }

 

highestOneBit()作用是取 i 这个数的二进制形式最左边的最高一位且高位后面全部补零,最后返回int型的结果。而lowestOneBit()自然就是取最右边的第一位1,其前面全部置0后的结果。代码类中还提供了其他的一些方法,这些方法也大量用到了位操作,有兴趣的可以自己去看一下。
来看一下类中其他重要的方法。

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. // 如下的这些ASCII字符可能会表示为数字.26个字符和10个数字,加起来是36
  2.    final static char[] digits = {
  3.        '0' , '1' , '2' , '3' , '4' , '5' ,
  4.        '6' , '7' , '8' , '9' , 'a' , 'b' ,
  5.        'c' , 'd' , 'e' , 'f' , 'g' , 'h' ,
  6.        'i' , 'j' , 'k' , 'l' , 'm' , 'n' ,
  7.        'o' , 'p' , 'q' , 'r' , 's' , 't' ,
  8.        'u' , 'v' , 'w' , 'x' , 'y' , 'z'
  9.    };
  10.    public static String toString(int i, int radix) {
  11.         // 基数的取值必须在一个范围,也就是2~36之间
  12.        if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX)
  13.            radix = 10;
  14.        /* Use the faster version */
  15.        if (radix == 10) {
  16.            return toString(i);//  按进制进行转换,默认为10进制
  17.        }
  18.        char buf[] = new char[33]; // 一个整数由32位来表示,负数时前面还需要一个“-”号
  19.        boolean negative = (i < 0);
  20.        int charPos = 32;
  21.        // 当i为一个正数时,变为负数
  22.        if (!negative) {
  23.            i = -i;
  24.        }
  25.        while (i <= -radix) {
  26.            buf[charPos--] = digits[-(i % radix)];
  27.            i = i / radix;
  28.        }
  29.        buf[charPos] = digits[-i];
  30.        if (negative) {
  31.            buf[--charPos] = '-';
  32.        }
  33.        return new String(buf, charPos, (33 - charPos));
  34.    }

如上源代码片段首先定义了一个digits字符数组,这些数组是可以表示数值类型的。如在十六进制中,a可以表示10,b可以表示11一样,26个字母同样可以代表一个两位数的整数,加上10个数字后,共有36个字符可以用来表示数值。由此可知,Java中支持的最大进制为32,如果大于32,则按10进制进行处理。接下来有一个toString()方法,功能是将十进制的i转换为radix进制的数,并以字符串的形式进行返回。如下测试代码:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. System.out.println(Integer.toString(13,19));
  2. System.out.println(Integer.toString(173,29));

运行结果如下:d    // 13          5s  // 5*29+28

需要注意的是toString()方法的实现代码,可以看到对于一个有符号数i,都是转换为负数来进行进制转换的,这样可以统一进行有符号数的处理。那么可不可以转换为正数进行处理呢?这时候就需要考虑边界的问题了。举个例子,如byte类型,表示的最小整数为-128,而最大的整数为127,这时候将负数转换为正数就会超出类型所表示的范围,在这里的道理是一样的。
看一下将数值默认按10进制进行字符串转换的toString()方法:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public static String toString(int i) {
  2.        if (i == Integer.MIN_VALUE)
  3.            return "-2147483648";
  4.        //举例:如果是9,这时size为1,而如果为-9时,为2,还要存储一个"-"号
  5.        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);// 计算出保存整数需要的字符数组大小
  6.        char[] buf = new char[size];
  7.        getChars(i, size, buf);
  8.        return new String(0, size, buf);
  9.    }
  10.    //如下存储的是10进制的数
  11.    final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
  12.        99999999, 999999999, Integer.MAX_VALUE };// 最大的MAX_VALUE为2147483647
  13.    // Requires positive x
  14.    static int stringSize(int x) {
  15.        for (int i=0; ; i++)v高效率,采用查表法
  16.                return i+1;
  17.    }

功能就是将一个有符号的整数转换为字符串,首先是将这个整数转换为字符数组,然后将这个字符数组转换为字符串即可。当i等于Integer.MIN_VALUE时,直接返回值,而不采用算法。这是因为调用getChars()方法进行转换时,最大的负数要换为正数进行计算,而这会造成溢出。

getChars()方法的源代码如下:

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. final static char [] DigitTens = {
  2.      '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
  3.      '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
  4.      '2', '2', '2', '2', '2', '2', '2', '2', '2', '2',
  5.      '3', '3', '3', '3', '3', '3', '3', '3', '3', '3',
  6.      '4', '4', '4', '4', '4', '4', '4', '4', '4', '4',
  7.      '5', '5', '5', '5', '5', '5', '5', '5', '5', '5',
  8.      '6', '6', '6', '6', '6', '6', '6', '6', '6', '6',
  9.      '7', '7', '7', '7', '7', '7', '7', '7', '7', '7',
  10.      '8', '8', '8', '8', '8', '8', '8', '8', '8', '8',
  11.      '9', '9', '9', '9', '9', '9', '9', '9', '9', '9',
  12.      } ;
  13.  final static char [] DigitOnes = {
  14.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  15.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  16.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  17.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  18.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  19.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  20.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  21.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  22.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  23.      '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
  24.  } ;
  25.  static void getChars(int i, int index, char[] buf) {
  26.      int q, r;
  27.      int charPos = index;
  28.      char sign = 0;
  29.      if (i < 0) {
  30.          sign = '-';
  31.          i = -i;
  32.      }
  33.      // Generate two digits per iteration
  34.      // 处理超过4个字节能表示的最大数范围的数,也就是处理Unicode的增补字符
  35.      while (i >= 65536) {// 2^16=65536
  36.          q = i / 100;
  37.          // 2^6+2^5+2^4=100
  38.          r = i - ((q << 6) + (q << 5) + (q << 2));// 相当于r = i - (q * 100);
  39.          i = q;
  40.          buf [--charPos] = DigitOnes[r];
  41.          buf [--charPos] = DigitTens[r];
  42.      }
  43.      // Fall thru to fast mode for smaller numbers
  44.      for (;;) {
  45.          q = (i * 52429) >>> (16+3);
  46.          r = i - ((q << 3) + (q << 1));  // r = i-(q*10) ...
  47.          buf [--charPos] = digits [r];
  48.          i = q;
  49.          if (i == 0) break;
  50.      }
  51.      if (sign != 0) {
  52.          buf [--charPos] = sign;
  53.      }
  54.  }

为了能够快速将数值转换为字符类型,程序采用了空间换时间的策略。DigitOnes 和DigitTwos数组,通过除100得到的余数匹配数组中的值就可以得到想要的char类型的数值。然后分别查DigitTens和DigitOnes两个表,直接获取十位和个位。

 

当i < 65536时,通过i - ( i / 10 * 10 )来获得每一位。

这里的 q = (i * 52429) >>> (16+3)可能会让人费解,实际2 ^ 19 = 524288,而52429 / 524288 = 0.10000038146972656,约为0.1,所以这一步实际就是在除以10,只不过是换种效率更高的方法而已。

值得一提的是除了52429外,还有很多数可以选,如:

2^10=1024, 103/1024=0.1005859375

2^11=2048, 205/2048=0.10009765625

...

2^18=262144, 26215/262144=0.10000228881835938

2^19=524288, 52429/524288=0.10000038146972656

当然,选52429也不是偶然,而是因为它是在不超出整型范围内,精度最高的一个。

最后就是看是否为负数,如果是负数的话,加入‘-’字符到buf字符数组中就可以解决问题了。

好了,我们继续往下看,再一个主要的方法就是decode()了,源代码如下:

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public static Integer decode(String nm) throws NumberFormatException {
  2.        int radix = 10;
  3.        int index = 0;
  4.        boolean negative = false;
  5.        Integer result;
  6.        if (nm.length() == 0)
  7.            throw new NumberFormatException("Zero length string");
  8.        char firstChar = nm.charAt(0);
  9.        // Handle sign, if present
  10.        if (firstChar == '-') {
  11.            negative = true;
  12.            index++;
  13.        } else if (firstChar == '+')
  14.            index++;
  15.        // Handle radix specifier, if present
  16.        if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {
  17.            index += 2;
  18.            radix = 16;
  19.        }
  20.        else if (nm.startsWith("#", index)) {
  21.            index ++;
  22.            radix = 16;
  23.        }
  24.        else if (nm.startsWith("0", index) && nm.length() > 1 + index) {
  25.            index ++;
  26.            radix = 8;
  27.        }
  28.        if (nm.startsWith("-", index) || nm.startsWith("+", index))
  29.            throw new NumberFormatException("Sign character in wrong position");
  30.        try {
  31.            result = Integer.valueOf(nm.substring(index), radix);
  32.            result = negative ? Integer.valueOf(-result.intValue()) : result;
  33.        } catch (NumberFormatException e) {
  34.            // If number is Integer.MIN_VALUE, we'll end up here. The next line
  35.            // handles this case, and causes any genuine format error to be
  36.            // rethrown.
  37.            String constant = negative ? ("-" + nm.substring(index))
  38.                                       : nm.substring(index);
  39.            result = Integer.valueOf(constant, radix);
  40.        }
  41.        return result;
  42.    }

如上函数的功能就是将字符串转换为整数,接受以八进制、十进制和16进制格式书写的字符串,测试如上方法:

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. System.out.println(Integer.decode("0x0011"));
  2. System.out.println(Integer.decode("0X0011"));
  3. System.out.println(Integer.decode("011"));
  4. System.out.println(Integer.decode("-011"));

最后运行的结果如下:19     19     9     -9

 

合法的字符串表示的数值最后都是以十进制的形式返回的,或者还可以使用parseInt()方法,源代码如下:

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. public static int parseInt(String s, int radix)throws NumberFormatException{
  2.        /*
  3.         * WARNING: This method may be invoked early during VM initialization
  4.         * before IntegerCache is initialized. Care must be taken to not use
  5.         * the valueOf method.
  6.         */
  7.        if (s == null) {
  8.            throw new NumberFormatException("null");
  9.        }
  10.        if (radix < Character.MIN_RADIX) {
  11.            throw new NumberFormatException("radix " + radix +  " less than Character.MIN_RADIX");
  12.        }
  13.        if (radix > Character.MAX_RADIX) {
  14.            throw new NumberFormatException("radix " + radix + " greater than Character.MAX_RADIX");
  15.        }
  16.        int result = 0;
  17.        boolean negative = false;
  18.        int i = 0, len = s.length();
  19.        int limit = -Integer.MAX_VALUE;
  20.        int multmin;
  21.        int digit;
  22.        if (len > 0) {
  23.            char firstChar = s.charAt(0);
  24.            if (firstChar < '0') { // Possible leading "+" or "-"
  25.                if (firstChar == '-') {
  26.                    negative = true;
  27.                    limit = Integer.MIN_VALUE;
  28.                } else if (firstChar != '+')
  29.                    throw NumberFormatException.forInputString(s);
  30.                if (len == 1) // Cannot have lone "+" or "-"
  31.                    throw NumberFormatException.forInputString(s);
  32.                i++;
  33.            }
  34.            multmin = limit / radix;
  35.            while (i < len) {
  36.                // Accumulating negatively avoids surprises near MAX_VALUE
  37.                digit = Character.digit(s.charAt(i++),radix);
  38.                if (digit < 0) {
  39.                    throw NumberFormatException.forInputString(s);
  40.                }
  41.                if (result < multmin) {
  42.                    throw NumberFormatException.forInputString(s);
  43.                }
  44.                result *= radix;
  45.                if (result < limit + digit) {
  46.                    throw NumberFormatException.forInputString(s);
  47.                }
  48.                result -= digit;
  49.            }
  50.        } else {
  51.            throw NumberFormatException.forInputString(s);
  52.        }
  53.        return negative ? result : -result;
  54.    }

由于这个函数是公开的,所以对边界条件的检查特别严格。将指定进制的有符号整数转换为十进制的整数返回,测试代码如下:

 

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. parseInt("0", 10) returns 0
  2. parseInt("473", 10) returns 473
  3. parseInt("+42", 10) returns 42
  4. parseInt("-0", 10) returns 0
  5. arseInt("-FF", 16) returns -255
  6. parseInt("1100110", 2) returns 102
  7. parseInt("2147483647", 10) returns 2147483647
  8. parseInt("-2147483648", 10) returns -2147483648
  9. parseInt("2147483648", 10) throws a NumberFormatException
  10. parseInt("99", 8) throws a NumberFormatException
  11. parseInt("Kona", 27) returns 411787

需要注意的是:

 

 

[java] view plaincopyprint?在CODE上查看代码片派生到我的代码片

  1. parseInt("Kona", 10) throws a NumberFormatException //因为十进制不可能出现K字符,所以出现异常

程序会出现错误,因为10进制的数值中只能出现0到9的数字,这主要是通过Character.digit()函数进行判断的。关于这个函数的源代码将在字符源码剖析中解析。

Integer中也提供了hashCode()、equals()、compareTo()等常用的方法,由于这些方法在前一篇文章 Java 7源码分析第2篇 - Java整数类型(1)中已经介绍过,所以不再介绍。

 

在Integer中还有许多其他的方法,有兴趣的读者可以自己去查看。


本文固定链接: http://www.devba.com/index.php/archives/4775.html | 开发吧

报歉!评论已关闭.