使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
day010 数组 (数组介绍、for each循环、数组初始化以及匿名数组、数组拷贝、命令行参数、数组排序、多维数组、不规则数组)
1.数组介绍
数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。例如,如果a是一个整型数组,a[i]就是数组中下标为i的整数。
在声明数组变量时,需要指出数组类型(数据元素类型紧跟[])和数组变量的名字。下面声明了整型数组:
int[] a;
不过,这条语句只声明了变量a,并没有将a初始化为一个真正的数组。应该使用new运算符创建数组。
int[] a = new int[100];
这条语句创建了一个可以存储100个整数的数组。数组长度不要求是常量:new int [n]会创建一个长度为n的数组。
声明数组还可以用 int a[ ]这种格式,不过大多数人还是喜欢用上面的,因为它将类型int[](整型数组)与变量名分开了。
这个数组的下标从0~99(不是1~100)。一旦创建了数组,就可以给数组元素赋值。例如,使用一个循环:
int[] a = new int[100];
for (int i = 0;i < 100;i++)
a[i] = i;
// fills the array with numbers 0 to 99
创建一个数字数组时,所有元素都初始化为0。boolean数组的元素会初始化为fals%对象数组的元素则初始化为一个特殊值null,这表示这些元素(还)未存放任何对象。
例如:
String[] name = new String[10];
会创建一个包含10个字符串的数组,所有字符串都为null。如果希望这个数组包含空串,可以为元素指定空串:
for (int i = 0;i < 10;i++)
name[i] = "";
如果创建了一个100个元素的数组,并且试图访问元素a[100](或任何在0~99之外的下标),程序就会引发异常而终止执行。
要想获得数组中的元素个数,可以使用array.length。例如,
for (int i = 0;i < a.length; i++)
System.out.println(a[i]);
一旦创建了数组,就不能再改变它的大小(尽管可以改变每一个数组元素)。如果经常需要在运行过程中扩展数组的大小,就应该使用另一种数据结构—数组列表。
2. for each循环
Java有一种功能很强的循环结构,可以用来依次处理数组中的每个元素(其他类型的元素集合亦可)而不必为指定下标值而分心。格式为:
for (variable : collection) statement
定义一个变量用于暂存集合中的每一个元素,并执行相应的语句(当然,也可以是语句块)。例如:
for (int element:a)
System.out.println(element);
打印数组a的每一个元素,一个元素占一行。
for each循环语句的循环变量将会遍历数组中的每个元素,而不需要使用下标值。
3.数组初始化以及匿名数组
在Java中,提供了一种创建数组对象并同时赋予初始值的简化书写形式。下面是一例子:
int[] a = {2,3,,5,7,11,13};
在使用这种语句时,不需要调用new。甚至还可以初始化一个匿名的数组:
new int[] {3,6,77};
这种表示法将创建一个新数组并利用括号中提供的值进行初始化,数组的大小就是初始值的个数。使用这种语法形式可以在不创建新变量的情况下重新初始化一个数组。例如:
a = new int[] {3,6,77};
上面的初始化就是简化了下面的数组初始化代码:
int b = {3,5,7,8};
a = b;
4.数组拷贝
在Java中,允许将一个数组变量拷贝给另一个数组变量。这时,两个变量将引用同一个数组:
int[] luckyNumbers = smallPrimes;
1uckyNumbers[5] = 12; // now smallPrimes[5] is also 12
如果希望将一个数组的所有值拷贝到一个新的数组中去,就要使用Arrays类的copyOf方法:
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);
第 2个参数是新数组的长度。这个方法通常用来增加数组的大小:
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, 2*luckyNumbers.length);
如果数组元素是数值型,那么多余的元素将被赋值为 0; 如果数组元素是布尔型,则将赋值为 false。相反,如果长度小于原始数组的长度,则只拷贝最前面的数据元素。
学过C\C++语言的同学应该记得指针运算,可以通过a+1得到数组的下一个元素,Java中的[]运算符被预定义为检查数组边界,而且没有指针运算,即不能通过a+1得到数组的下一个元素。
5.命令行参数
前面已经看到多个使用Java数组的示例。每一个Java应用程序都有一个带String arg[] 参数的 main方法。这个参数表明 main方法将接收一个字符串数组,也就是命令行参数。
例如下面这个代码:
public class Message
{
public static void main(String[] args)
{
if (args.length = 0 || args[0].equals("-h"))
System.out.print("Hello,");
else if (args[0].equals("-g"))
System.out.print("Goodbye,");
// print the other command-line arguments
for (int i =1 ; i < args.length; i ++)
System.out.print(" " + args[i]);
System.out.println("!");
}
}
如果使用下面这种形式运行这个程序:
java Message -g cruel world
这个跟普通的运行没区别。java Message是指运行Message.class.后面的3个-g cruel world是当做参数传入主方法的String类型数组的。因此这就是运行Message并且传入3个参数进去而已。
args数组将包含下列内容:
args[0] : "-g"
args[l] : "cruel"
args[2] : "world"
这个程序将显示下列信息: Goodbye,cruel world!
6.数组排序
要想对数值型数组进行排序,可以使用Arrays类中的sort方法:
int[] a = new int[10000];
///...
Arrays.sort(a)
这个方法使用了优化的快速排序算法。快速排序算法对于大多数数据集合来说都是效率比较高的。
下面这个程序用到了数组,它产生一个抽彩游戏中的随机数值组合。假如抽彩是从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)的随机浮点数。用乘以这个浮点数,就可以得到从0到n-l之间的一个随机数。
int r = (int)(Math.random() * n);
下面将result的第i个元素设置为numbers[r]存放的数值,最初是r+1。但正如所看到的,numbers数组的内容在每一次抽取之后都会发生变化。
result[i] = number[r];
现在,必须确保不会再次抽取到那个数值,因为所有抽彩的数值必须不相同。因此,这里用数组中的最后一个数值改写mimber[r],并将n减1。
numbers[r] = numbers[n-1];
n--;
关键在于每次抽取的都是下标,而不是实际的值。下标指向包含尚未抽取过的数组元素。在抽取了k个数值之后,就可以对result数组进行排序了,这样可以让输出效果更加清晰:
Arrarys.sort(result);
for(int r :result)
System.out.println(r);
完整的代码:
import java.util.Arrays;
import java.util.Scanner;
public class Message
{
public static void main(String[] args)
{
Scanner in =new Scanner(System.in);
System.out.print("How many numbers do you need to draw? ");
int k = in.nextInt();
System.out.print("What is the highest number you can draw? ");
int n = in.nextInt();
// fill an array with numbers 1 2 3 . . . n
int[] numbers = new int[n];
for (int i = 0;i < numbers.length;i++)
numbers[i]=i+ 1;
//draw k numbers and put them into a second array
int[] result = new int[k];
for (int i = 0;i < result.length;i++)
{
//make a random index between 0 and n - 1
int r =(int) (Math.random()*n);
//pick the element at the random location
result[i]= numbers[r];
//move the last element into the random location
numbers[r]= numbers[n - 1 ];
n--;
}
//print the sorted array
Arrays.sort(result);
System.out.println("Bet the following combination. It'll make you rich!");
for (int r : result)
System.out.println(r);
}
}
看看API注释中Arrays类
需要记下常用的几种形式。
7.多维数组
多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式。
在Java中,声明一个二维数组相当简单。例如:
double [][] balances;
与一维数组一样,在调用new对多维数组进行初始化之前不能使用它。在这里可以这样初始化:
balances = new double[NYEARS][NRATES]
另外,如果知道数组元素,就可以不调用new,而直接使用简化的书写形式对多维数组进行初始化。例如:
int[][] magicSquare =
{
{16, 3, 2, 13},
{5, 10, 11, 8},
{9, 6, 7, 12},
{4, 15, 14, 1 }
};
一旦数组被初始化,就可以利用两个方括号访问每个元素,例如,balances[i][j]。
要想快速地打印一个二维数组的数据元素列表,可以调用:
System.out.println(Arrays.deepToString(a));
输出格式为:
[[16,3, 2, 13], [5, 10, 11, 8], [9, 6, 7, 12], [4, 15, 14, 1 ]]
8.不规则数组
到目前为止,我们所看到的数组与其他程序设计语言中提供的数组没有多大区别。但实际存在着一些细微的差异,而这正是Java的优势所在:Java实际上没有多维数组,只有一维数组。多维数组被解释为“数组的数组。”
表达式balancesfi]引用第i个子数组,也就是二维表的第i行。它本身也是一个数组,balances[i][j]引用这个数组的第j项。
由于可以单独地存取数组的某一行,所以可以让两行交换。
double[] temp = balances[i];
balances[i] = balances[i+ 1 ];
balances[i+ 1] = temp;
还可以方便地构造一个“不规则”数组,即数组的每一行有不同的长度。要想创建一个不规则的数组,首先需要分配一个具有所含行数的数组。
int[][] odds = new int [NMAX + 1][];
接下来,分配这些行。
for (int n = 0; n <= NMAX; n++)
odds[n] = new int[n + 1];
在分配了数组之后,假定没有超出边界,就可以采用通常的方式访问其中的元素了。
for (int n = 0; n < odds.length; n++)
for (int k = 0; k < odds[n].length; k++)
{
//compute lotteryOdds
//...
odds[n] [k] = lotteryOdds;
}
这样就创建了一个不规则数组。
现在,已经学到Java语言的基本程序结构,后面就要开始学习Java中的面向对象的程序设计了。(有C#的基础学起来应该比较轻松)