参考自–《Java核心技术卷1》
大数值和数组
1 大数值
如果基本的整数和浮点数精度不能够满足需求,可以使用 java.math
包中的两个很有用的类:BigInteger
和 BigDecimal
。这两个类可以处理包含任意长度数字序列的数值。
BigInteger
类实现了任意精度的整数运算;BigDecimal
实现了任意精度的浮点数(实数)运算。
使用静态的 valueOf
方法可以将普通的数值转换为大数值:
BigInteger a = BigInteger.valueOf(100);
BigInteger b = BigInteger.valueOf(1);
但,不能使用普通的算术运算符(如:+ 和 *)处理大数值。而是需要使用大数值中的方法进行运算:
BigInteger c = a.add(b); // 101
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2))); // 303
注:大数值运算的两边都需要是大数值。
常用方法:
- BigInteger add(BigInteger other) 和
- BigInteger subtract(BigInteger other) 差
- BigInteger mutiply(BigInteger other) 积
- BigInteger divide(BigInteger other) 商
- BigInteger mod(BigInteger other) 余数
- int compareTo(BigInteger other) 如果这个大整数与other相等,返回0;如果这个大整数小于other,返回负数;否则,返回整数
- static BigInteger valueOf(long x) 返回值等于x的大整数
BigDecimal
的方法用法大致与 BigInteger
相同,不同:
- BigDecimal divide(BigDecimal other RoundingMode mode) 计算商时,必须给出舍入方式(RoundingMode mode)。
RoundingMode.HALF_UP
是常规的四舍五入方式。
2 数组
数组是最常见的一种数据结构,用于存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个元素。
数组的声明:声明数组变量时,需要指出数组类型和数组变量的名字
int[] a; // 声明了int型数组 a
//int a[]; 两种方式等效,习惯上使用上面的声明方式
数组的初始化:使用 new 运算符创建数组
int[] a = new int[100]; // 创建了可以存储100个整数的数组
数组长度不要求是常量:new int[n]
会创建一个长度为n的数组。
获取数组中元素个数:array.length
注:创建一个数字数组时,所有元素都初始化为0;boolean数组的元素会初始化为false;对象数组的元素则初始化为一个特殊值null,这表示这些元素还没有存放任何对象(如 String 数组)。如需要给数组元素赋值,可以遍历数组赋值。
注:一旦创建了数组,就不能再改变它的大小(长度)。如果需要在使用中扩展数组的大小,可以使用另一种数据结构–数组列表(arraylist)。
2.1 for each 循环
Java有一种功能很强的循环结构,可以用来依次处理数组中的每一个元素(其他类型的元素亦可)而不必为指定下标值分心。
这种增强的 for 循环的语句格式为:for(variable:condition) statement
它定义一个变量用于暂存集合中的每一个元素,并执行相应的循环语句。condition 这一集合表达式必须是一个数组或者是一个实现了 Iterable 接口的类对象。如:
for(int element:a){
System.out.println(element);
}
//打印整型数组a中的每一个元素
for each 循环语句的循环变量将会遍历数组中的每个元素,而不需要使用下标;但在很多情况下,还是需要使用传统的 for 循环。
注:可以使用 Arrays 类的 toString
方法更加简便地打印数组中的所有值。
2.2 数组初始化以及匿名数组
在 Java 中可以创建数组对象并同时赋予初始值(这种情况下不需要调用 new):
int[] a = {1,2,3,4,5,6};
还可以初始化一个匿名的数组:
new int[] {7,8,9};
这种表示法将创建一个新数组并利用括号中提供的值进行初始化,数组的大小就是初始值的个数。使用这种语法形式可以在不创建新变量的情况下重新初始化一个数组:
a = new int[]{1,2,3,4,5,6,7,8,9}; //重新初始化数组a
简写:
int[] b = {1,2,3,4,5,6,7,8,9};
a = b;
注:Java 允许数组长度为0。在编写一个结果为数组的方法时,如果碰巧结果为空,则这种语法形式就显得非常有用。此时可以创建一个长度为0的数组:new elements[0]
。需要注意的是,数组长度为0与 null 不同。
2.3 数组拷贝
Java 允许将一个数组变量拷贝给另一个数组变量。这时,两个变量将引用同一个数组:
int[] luckNumbers = smallPrimes;
luckNumbers[5] = 12; //此时smallPrimes[5]的值也是12
//原理如下图:
如果希望将一个数组的所有值都拷贝到一个新的数组中去,可以使用 Arrays 类的 copyOf
方法:
int[] copy = Arrays.copyOf(luckNumbers, luckNumbers.length);
第2个参数是新数组的长度。通常可以借助这个方法的第二个参数来增加数组的长度:
luckNumbers = Arrays.copyOf(luckNumbers, 2*luckNumbers.length);
//增加数组的长度为原来的两倍
如果数组元素是数值型,那么多余的元素将被赋值为0;如果是布尔型,则将赋值为 false。
相反,如果长度小于原始数组的长度,则只拷贝最前面的数组元素。
2.4 数组排序
要想对数值型数组进行排序,可以使用 Arrays 类的 sort 方法:
int[] a = new int[100];
...
Arrays.sort(a);
这个方法使用了优化的快速排序算法。快速排序算法对于大多数数据集合来说都是效率比较高的。
Arrays 类的其它方法:
static String toString(type[] a)
返回包含在a中数据元素的字符串,这些元素被放在括号内,并用括号分隔。static type copyOf(type[] a,int length)
length表示拷贝数据元素的长度。如果length的值大于a.length
,结果为0或false;否则,数组中只有前面length个数组元素的拷贝值。static type copyOfRange(type[] a,int start,int end)
返回与a类型相同的数组,长度为end-start,(start起始下标,包含;end终止下标,不包含这个值,若end大于a.length
,结果为0或false).static void sort(type[] a)
采用优化的快速排序算法对数组进行排序static int binarySearch(type[] a,type v)
static int binarySearch(type[] a,int start,int end,type v)
采用二分搜索算法查找值v。如果查找成功,返回相应的下标值;否则,返回一个负数值r。-r-1 是保持a有序v应该插入的位置。(start起始下标,包含;end终止下标,不包含这个值)static void fill(type[] a,type v)
将数组的所有数据元素值设置为v.static boolean equals(type[] a,type[] b)
如果两个数组大小相同,并且下标相同的元素都对应相等,返回true。
2.5 多维数组
多维数组使用多个下标访问数组元素,适用于表示表格或者更加复杂的排列形式。下面以二维数组为例:
声明:
double[][] balances; //声明一个double类型的二维数组
初始化(在调用new对多维数组进行初始化之前不能使用它):
balances = new double[n][m]; //n为二维数组的列标,m为二维数组的行标
//或者这样初始化
int[][] magicSquare =
{
{1,2,3},
{4,5,6},
{7,8,9}
};
一旦数组被初始化,就可以利用两个方括号访问每个元素,例如,balances[i][j]
.
遍历:
-
for each循环语句不能自动处理二维数组的每一个元素。它是按照行处理。但是可以使用两个嵌套的循环遍历:
for(double[] row : a) for(double[] value : row) do something
-
想要快速地打印一个二维数组的数据元素列表,可以调用:
System.out.println(Arrays.deepToString(magicSquare)); //输出格式为 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
2.6 不规则数组
实际上,Java 并没有多维数组,只有一维数组。多维数组被解释为“数组的数组”:如下
balances 数组实际上是一个包含10个元素的数组,而每个元素又是一个由6个浮点数组成的数组。
不规则数组,即数组的每一行都有不同的长度:
final int MAX = 5;
int[][] odds = new int[MAX+1][];
//构建数组
for(int n = 0;n <= MAX; n++){
odds[n] = new int[n+1];
}
//填充数组元素
for(int n=0;n<=MAX;n++){
for(int k = 0; k < odds[n].length; k++) {
int lotterOdds = 1;
for(int i = 1; i < k; i++) {
lotterOdds = lotterOdds*(n-i+1)/i;
}
odds[n][k] = lotterOdds;
}
}
//打印
for (int[] row : odds){
for(int odd : row){
System.out.printf("%4d",odd);
}
System.out.println();
}
//结果
1
1 1
1 1 2
1 1 3 3
1 1 4 6 4
1 1 5 10 10 5