如果基本的整数和浮点数精度不能满足需求,就可以使用java.math包中的两个很有用的类: BigInteger和BigDecimal。这两个类可以处理任意长度的数值。BigInteger类实现了任意精度的整数运算,BigDecimal实现了任意精度的浮点数运算。 使用静态的valueOf方法可以把普通的数值转换为大数值: BigInteger a = BigInteger.valueOf(100); 可惜的是,不能使用人们熟悉的算术运算符(如+和*)处理大数值,而需要使用大数值类中的add和multiply方法。 BigInteger c = a.add(b); // c = a + b BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // d = c * (b + 2) 例3-6是对例3-5的中奖概率程序的改进,使其可以采用大数值进行运算。假设你被邀请参加抽奖活动,并从490个可能的数值中抽取60个,那么这个程序会得出中奖概率是716 395 843 461 995 557 415 116 222 540 092 933 411 717 612 789 263 493 493 351 013 459 481 104 668 848次中1次。祝你好运! 在例3-5程序中,用于计算的语句是 lotteryOdds = lotteryOdds * (n - i + 1) / i; 如果使用大数值,相应的语句为: lotteryOdds=lotteryOdds.muItiply(BigInteger.valueOf(n-i+1)).divide(BigInteger.valueOf(i)); 例3-6 BigIntegerTest.java java.math.BigInteger 1.1 ? BigInteger add(BigInteger other) ? BigInteger subtract(BigInteger other) ? BigInteger multiply(BigInteger other) ? BigInteger divide(BigInteger other) ? BigInteger mod(BigInteger other) 返回这个大整数和另一个大整数的和、差、积、商以及余数。 ? int compareTo(BigInteger other) 如果这个大整数与另一个大整数相等,则返回0;如果小于另一个大整数,则返回负数;如 果大于另一个大整数,则返回正数。 ? static BigInteger valueOf(long x) 返回值等于x的大整数。 java.math.BigDecimal 1.1 ? BigDecimal add(BigDecimal other) ? BigDecimal subtract(BigDecimal other) ? BigDecimal multiply(BigDecimal other) ? BigDecimal divide(BigDecimal other,RoundingMode mode) 5.0 返回这个大小数与另一个大小数的和、差、积、商。要想计算商,必须给出舍入方式 (rounding mode)。RoundingMode.HALF_UP是在学校中学习的四舍五入方式(即数值0到4舍去,数 值5到9进位)。它适用于常规的计算。有关其他的舍入方式请参阅API文档。 ? int compareTo(BigDecimal other) 如果这个大小数与另一个大小数相等,则返回0;如果小于另一个大小数,则返回负数;如 果大于另一个大小数,则返回正数。 ? static BigDecimal valueOf(long x) ? static BigDecimal valueOf(long x,int scale) 返回值为x或x/10scale的一个大小数。 数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。例如,如果a是一个整型数组,那么a[i]是数组中下标为i的整数。 在声明数组变量时,需要指出数组类型(数组元素类型后跟[ ])和数组的名字。下面声明了整型数组a: int[ ] a; 这条语句只声明了变量a,并没有把a初始化为一个真正的数组。应该使用new运算符来创建数组。 int[ ] a = new int[100]; 这条语句创建了一个可以存储100个整数的数组。 注意:可以使用下面两种形式中的任何一种声明数组 int[ ] a; 或 int a[ ]; 大多数Java程序员喜欢使用第一种风格,因为它将元素类型int[ ](整型数组)与变量名清晰分开了。 这个数组的下标从0~99(而不是从1~100)。一旦创建了数组,就可以给数组元素赋值。 例如,使用一个循环: int[ ] a = new int[100]; for (int i = 0; i < 100; i++) a[i] = i; //fills the array with 0 to 99 要想获得数组中的元素个数,可以使用array.length。例如, for (int i = 0; i < a.length; i++) System.out.println (a[i]); 一旦创建了数组,就不能再改变它的大小(当然,尽管可以改变每一个数组元素)。如果经常需要在运行过程中扩展数组的大小,就应该使用另一个数据结构—数组列表。(有关数组列表的详细内容请参看第5章。) “for each”循环 JDK 5.0增加了一种功能很强的循环结构,可以用来依次处理数组中的每个元素(其他类型的元素集合亦可)而不必为指定下标值而分心。 这种for循环的语句格式为: for (variable : collection) statement 定义一个变量用于暂存集合中的每一个元素,并执行相应的语句(当然,也可以是语句块)。集合表达式必须是一个数组或者是一个实现了Iterable接口的类(例如ArrayList)对象。有关数组列表的内容将在第5章中讨论,有关讨论Iterable接口的内容将在卷II的第2章中讨论。 例如, for (int element : a) System.out.println(element); 打印数组a的每一个元素,一个元素占一行。 这个循环应该读作“循环a中的每一个元素”(for each element in a)。Java语言的设计者认为应该使用诸如foreach、in这样的关键字,但这种循环语句并不是最初就包含在Java语言中的,而是后来添加进去的,并且没有人打算废除已经包含同名(例如System.in)方法或变量的旧代码。 当然,使用传统的for循环也可以获得同样的效果: for (int i = 0; i < a.length; i++) System.out.println(a[i]); 但是,“for each”循环语句显得更加简洁、更不易出错。(不必为下标的起始值和终止值而操心。) 如果需要处理一个集合中的所有元素的话,“for each”循环语句对传统循环语句的改进更是 叫人称赞不已。然而,在很多情况下,还是需要使用传统的for循环。例如,如果不希望遍历整 个集合,或者在循环内部需要操作下标值就需要使用传统的for循环。 数组初始化器以及匿名数组 在Java中,提供了一种创建数组对象并同时赋予初始值的简化书写形式。下面是一个语法例子: int[ ] smallPrimes = {2, 3, 5, 7, 11, 13}; 请注意,在使用这种语句时,不需要调用new。 甚至还可以初始化一个匿名数组: new int[ ] {17, 19, 23, 29, 31, 37} 这种表示法将创建一个新数组并利用括号中提供的值进行初始化,数组的大小就是初始值的个数。使用这种语法形式可以在不创建新变量的情况下重新初始化一个数组。例如: smallPrimes = new int[ ] {17, 19, 23, 29, 31, 37}; 它是下列语句的简写形式: int[ ] anonymous = {17, 19, 23, 29, 31, 37}; smallPrimes = anonymous; 数组拷贝 在Java中,允许将一个数组变量拷贝给另一个数组变量。这时,两个变量将引用同一个数组: int[ ] luckyNumbers = smallPrimes; luckyNumbers[5] = 12; // now smallPrimes[5] is also 12 图3-15显示了拷贝的结果。如果希望将一个数组的所有值拷贝到另一个数组中去,就要使用System类的arraycopy方法。调用这个方法的语法格式为: System.arraycopy(from, fromIndex, to, toIndex, count); 数组to必须有足够的空间来存放拷贝的元素。 例如,下面这段语句所得到的结果如图3-16所示。它创建了两个数组,然后将第一个数组的后4个元素拷贝到第2个数组中。拷贝从源数组的第2个位置开始,一共拷贝4个元素,目标数组的起始位置为3。 输出结果为: 命令行参数 前面已经看到多次使用Java数组的例子。每一个Java应用程序都有一个带String [ ] args参数的main方法。这个参数表明main方法将接收一个字符串数组,也就是命令行参数。 例如,看看下面这个程序: 如果使用下面这种形式调用这个程序: java Message -g cruel world args数组将包含下列内容: args[0]: "-g" args[1]: "cruel" args[2]: "world" 这个程序将显示下列信息: Goodbye, cruel world! 数组排序 要想对数值型数组进行排序,可以使用Arrays类中的sort方法: int[ ] a = new int[10000]; . . . Arrays.sort(a) 这个方法使用了优化的快速排序算法。快速排序算法对于大多数数据集合来说都是效率比较高的。Arrays类还提供了几个使用起来很便捷的方法,在稍后的API注释中将给出这些方法。 例3-7中的程序将产生一个抽奖游戏中的随机数值组合。假如抽奖是“从49个数值中抽取6个”,那么,程序可能的输出结果为: Bet the following combination. It’ll make you rich! 4 7 8 19 30 44 要想选择这样一个随机的数值集合,就要首先将数值1,2,…,n存入数组numbers中: int[ ] numbers = new int[n]; for (int i = 0; i < numbers.length; i++) numbers[i] = i + 1; 而用第二个数组存放抽取出来的数值: int[ ] result = new int[k]; 现在,就可以开始抽取k个数值了。Math.random方法将返回一个0~1之间(含0、不含1)的随机浮点数。用n乘以这个浮点数,就可以得到从0~n-1之间的一个随机数。 int r = (int)(Math.random( ) * n); 下面将result的第i个元素设置为numbers[r]存放的数值,最初就是r本身。但正如所看到的那样,numbers数组的内容在每一次抽取之后都会发生变化。 result[i] = numbers[r]; 因为所有抽奖的数值不能一样,所以必须确保不会再次抽取到那个数值。因此,这里使用数组中的最后一个数值改写number[r],并将n减1。 numbers[r] = numbers[n - 1]; n--; 关键在于每次抽取的都是下标,而不是实际的值。下标指向(point into)包含尚未抽取过的数组元素。 在抽取了k个数值之后,就可以对result数组进行排序了,这样使得输出效果更加清晰: Arrays.sort(result); for (int r: result) System.out.println(r); 例3-7 LotteryDrawing.java java.lang.System 1.1 ? static void arraycopy(Object from, int fromIndex, Object to, int toIndex, int count)将第一个数组中的元素拷贝到第二个数组中。 参数:from 任意类型的数组(在第5章中将解释为什么这个参数的类型是Object) fromIndex 原始数组中待拷贝元素的起始下标 to 与from同类型的数组 toIndex 目标数组放置拷贝元素的起始下标 count 拷贝的元素数量 java.util.Arrays 1.2 ? static void sort(type[ ] a) 使用优化的快速排序算法对数组进行排序。 参数:a 一个类型为int、long、short、char、byte、float或double的数组 ? static int binarySearch(type[ ] a, type v) 使用二分搜索算法查找值v。如果找到,返回相应的下标;否则,返回一个负值r;-r~1为保持有序v应插入的位置。 参数:a 一个类型为int、long、short、char、byte、float或double的有序数组v 与a中元素类型相同的一个值 ? static void fill(type[ ] a, type v) 将数组的所有元素设置为v。 参数:a 一个类型为int、long、short、char、byte、boolean、float或double的数组v 与a中元素类型相同的一个值 ? static boolean equals(type[ ] a, type[ ] b) 如果两个数组的长度相同,并且下标相同的元素都对应相等,则返回true。 参数:a, b 类型为int、long、short、char、byte、boolean、float或double的数组 多维数组 多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式。可以跳过这一节的内容,等到需要使用这种存储机制时再返回来学习。 假设需要建立一个数值表,用来显示在不同利率下投资$10 000会增长多少,利息每年兑现,而且又被用于投资。表3-8是相应的图示。 可以使用一个二维数组(即矩阵)存储这些信息。这个数组被命名为balances。 在Java中,声明一个二维数组相当简单。例如: double[ ][ ] balances; 与一维数组一样,在调用new对多维数组进行初始化之前不能使用该数组。在这里可以这样进行初始化: balances = new double[NYEARS][NRATES]; 在其他情况下,如果知道数组元素,就可以不调用new,而直接使用简写形式对多维数组进行初始化。例如: 一旦数组被初始化,就可以通过两个方括号访问每个元素,例如,balances[i][j]。 在例子程序中用到了一个存储利率的一维数组interests与一个存储余额的二维数组balances,一维用于表示年,另一维用于表示利率。最初使用初始余额来初始化这个数组的第一行: 然后,按照下列方式计算其他行: 例3-8给出了完整的程序。 例3-8 CompoundInterest.java 不规则数组 至此为止,所看到的数组与其他程序设计语言中提供的数组没有多大区别。但实际存在着一些细微的差异,而这正是Java的优势所在:Java实际上没有多维数组,只有一维数组。多维数组解释为“数组的数组。” 例如,在前面的例子中,balances数组实际上是一个包含10个元素的数组,而每个元素又是一个由6个浮点数组成的数组(请参看图3-17)。 表达式balances[i]引用第i个子数组,也就是二维表的第i行。它本身也是一个数组,balances[i][j]引用这个数组的第j项。 由于可以单独地存取数组的某一行,所以可以对两行进行交换! 还可以很方便地构造一个“不规则”数组,即数组的每一行有不同的长度。下面是一个典型的例子。在这个例子中,创建一个数组,第i行第j列将存放“从i个数值中抽取j个数值”产生的结果。 因为j不可能大于i,所以矩阵是三角形的。第i行有i + 1个元素。(允许抽取0个元素,这也是一种选择。)要想创建一个不规则的数组,首先需要分配一个具有所含行数的数组。 接下来,分配这些行。 在分配了数组之后,假定没有超出边界,就可以采用通常的方式访问其中的元素了。 例3-9给出了完整的程序。 例3-9 LotteryArray.java大数值
数组
觉得文章不错的话,可以转发此文关注小编,后续持续更新干货文章