数组的定义和使用大多和C是一样的。数组是存放一类数据的一种数据结构。Java中的数组(Array)是一种特殊的对象(通过类实例化),也有特殊的语法结构。在学习Array的时候,请注意其语法,以及与后续类和对象的区别。 ## 1. 声明与使用 ### 1.1. 声明与使用数组 下面这个例子说明了数组的基本使用。 ```java public class ArrayTest { public static void main(String[] args) { int[] myList; myList = new int[2]; myList[0] = 0; myList[1] = 1; System.out.println(myList[1]); myList = new int[3]; System.out.println(myList[1]); } } ``` Java中数组的声明与使用与C类似,`int[] myList` 定义了一个可以存放int数据类型的数组,但是此时还没有为这个数组分配内存。这一点和C的指针类是,其实myList这个变量叫做引用变量,也和指针类似。 要为myList这个数组分配内存,使用 `new int[2]` 这种类似的语法。该语法返回一个数组对象的引用地址,数组的容量是2,并赋值给引用变量 myList,然后就可以通过引用变量 myList对数组进行操作了。 可以重复对一个数组引用变量使用new语句分配内存,例如,例子中第6行和第12行。与C不同的是,Java不用对动态分配的内存进行回收。   **特别注意这里new的语法结构,后面是数组定义时的类型,大小设置不是圆括号,而是方括号!** ### 1.2. 声明和分配内存在一行中 ```java public class ArrayTest2 { public static void main(String[] args) { int[] myList = new int[2]; myList[0] = 0; myList[1] = 1; System.out.println(myList[1]); } } ``` ### 1.3. 在声明的同时初始化数组 这个语法结构和C类似: ```java public class ArrayTest3 { public static void main(String[] args) { int[] myList = {1,3,5,7}; System.out.println(myList[1]); } } ``` **注意,大括号赋初值的语法只能在数组声明中使用,不能单独使用。** ### 1.4. 数组元素的缺省值 When an array is created, its elements are assigned the default value of 0 for the numeric primitive data types, '\u0000' for char types, and false for boolean types. ## 2. 数组操作 ### 2.1. 数组对象上的操作 数组本质上是一个对象,因此直接可以在数组的引用变量上对数组进行操作。例如,取得数组的长度: ```java public class ArrayTest2 { public static void main(String[] args) { int[] myList = {1,3,5,7}; System.out.println(myList.length); } } ``` myList 作为一个对象,还有很多实例函数,只需要在IDE中写入这个引用变量,并在后面打一个点,就会出现这个对象的所有操作(可能包括变量、函数和常量)。 ![](img/2023-03-04-13-50-32-image.png) 有兴趣的可以研究一下这些函数的作用,其实从这些函数名就可以知道大约的作用。 ### 2.2. 数组元素的初始值 When an array is created, its elements are assigned the default value of 0 for the numeric primitive data types, '\u0000' for char types, and false for boolean types. ### 2.3. 加强的for循环 和C一样,Java数组的下标也是从0开始的;结合数组的长度,可以对数组进行遍历。 ```java public class Test { public static void main(String[] args) { int arrayInt[] = { 1, 3, 5, 7 }; for (int i = 0; i < arrayInt.length; i++) { System.out.println(arrayInt[i]); } } } ``` Java还提供了一种更方便的for循环: ```java public class ArrayTest2 { public static void main(String[] args) { int[] myList = { 1, 3, 5, 7 }; for (int i : myList) { System.out.println(i); } } } ``` 这个加强版的for循环例子如上,myList是一个数组(更准确的说是一个数据集),每次取得一个数据集的元素并保存在变量i中(注意这个临时变量i的类型必须和数组中存放数据的类型一致),直到所有的元素被遍历。 **注意,这个for循环在以后会大量用到。** ### 2.4. 数组变量是引用变量(指针) ![](img/20230304124145.png) 对数组赋值,会改变引用变量的内存指向,这和C的指针是一个概念。 **注意:Java中不存在手动回收内存,不存在指针悬空的情况。** ### 2.5. 使用函数来复制数组 可以使用循环来复制数组: ```java public class Test { public static void main(String[] args) { int[] sourceArray = { 2, 3, 1, 5, 10 }; int[] targetArray = new int[sourceArray.length]; for (int i = 0; i < sourceArray.length; i++) targetArray[i] = sourceArray[i]; } } ``` Java在System这个类中提供了一个更加通用的数组复制函数`arraycopy`,注意这个函数是静态函数。 ```java public class ArrayTest2 { public static void main(String[] args) { int[] myList1 = { 1, 3, 5, 7 }; int[] myList2 = new int[10]; System.arraycopy(myList1, 0, myList2, 0, 3); for (int i : myList2) { System.out.println(i); } } } ``` 注意 System.arraycopy 这个函数的参数代表什么意思。 ### 2.6. 数组作为函数参数 ```java public class ArrayTest3 { public static void main(String[] args) { int[] iA = { 5, 6, 7, 8 }; System.out.println("The original array:"); printArray(iA); changeVal(iA); System.out.println("After change item value in function:"); printArray(iA); changeArray(iA); System.out.println("After change array in function:"); printArray(iA); } public static void printArray(int[] a) { for (int i : a) { System.out.println(i); } } public static void changeVal(int[] a) { for (int i = 0; i < a.length; i++) { a[i] = i; } } private static void changeArray(int[] a) { a = new int[3]; for (int i = 0; i < a.length; i++) { a[i] = i + 100; } } } ``` **注意:可以在函数中改变数组内部元素的值。** ### 2.7. 对数组元素排序 Arrys这个类中提供对数组进行排序的函数: ```java public class Test { public static void main(String[] args) { double[] numbers = { 6.0, 4.4, 1.9, 2.9, 3.4, 3.5 }; java.util.Arrays.sort(numbers); for (double item : numbers) System.out.print(item + " "); } } ``` 排序后,数组中的元素按照从小到达排列。 ### 2.8. 数组元素的查找 我们可以使用循环来查找数组中某个元素的位置,其实Java已经给我们提供了一个数组查找的函数`binarySearch`。注意这里的binary是指二分法排序,而不是指二进制。如果采用二分法查找,那么数组必须是有序的。如果无法确保数组是有序的,使用前面的`java.util.Arrays.sort`函数先对数组进行排序。 ```java public class Test { public static void main(String[] args) { int[] list = { 2, 4, 7, 10, 11, 45, 50, 59, 60, 66, 69, 70, 79 }; int pos = -1; pos = java.util.Arrays.binarySearch(list, 11); System.out.println("Index is " + pos); } } ``` 这个函数就是binarySearch,这个函数指使用注意这个函数的使用,前面的一窜`java.util.Arrays` 不是都指类的名字;最有最后的 `Arrays` 是类名,而 `java.util` 是命名空间。通常情况下,可以简单的理解命名空间就是目录的层级关系。 关于`java.util.Arrays`这个类还有很多其他函数,可以在IDE中通过输入点(.)查看: ![](img/2023-03-04-15-49-20-image.png) 有兴趣的同学可以自己去研究一下。 ### 2.9. 补充 这些内容可能考试不会直接考到,但是会方便你的编程,希望你们可以掌握。 关于String的操作,通常会用到把一个字符串转换成数组。例如一个以空格分隔的字符串,我们希望把每一段切取出来,形成一个数组,这时我们使用: ```java String string = "AAA BBB CCC"; string.split(" "); ``` split的参数是一个正则表达式(这个我们可以不用掌握),这里如果字符串是空格分隔,写入一个空格的字符串就可以了,标识拆分的字符串。 函数返回是一个 String\[\] 类型的数组,你得到的是 {"AAA", "BBB", "CCC" } ,注意,不包含分隔字符串。 相对的,可以使用 join 函数把一个字符串数组以一定的分隔符连接成一个新的字符串。 split 是实例函数,join是静态函数。 ## 3. 读取命令行参数 细心的同学们可能注意到了,在主函数中有一个字符串的数组,这个是做什么的?其实这是传递程序运行是输入的命令行参数设计的,我们看看下面这个例子: ```java public class Calculator { /** Main method */ public static void main(String[] args) { // Check number of strings passed if (args.length != 3) { System.out.println("Usage: java Calculator operand1 operator operand2"); System.exit(0); } // The result of the operation int result = 0; // Determine the operator switch (args[1].charAt(0)) { case '+': result = Integer.parseInt(args[0]) + Integer.parseInt(args[2]); break; case '-': result = Integer.parseInt(args[0]) - Integer.parseInt(args[2]); break; case '.': result = Integer.parseInt(args[0]) * Integer.parseInt(args[2]); break; case '/': result = Integer.parseInt(args[0]) / Integer.parseInt(args[2]); } // Display result System.out.println(args[0] + ' ' + args[1] + ' ' + args[2] + " = " + result); } } ``` 主函数`void main(String[] args)` 中有有一个args 的字符串数组。编译这个程序,然后在命令行下执行,如果不记得命令行中如何编译和运行java,请参考第一章中的内容。 ![](img/2023-03-04-15-41-18-image.png) 在命令行中运行编译好的代码 `java Calculator 1 + 21` 其中 java是指虚拟机,后面的 `Calculator` 是Java编译后的可执行代码;`1 + 21` 是命令行参数。这个参数会传递给Calculator的主程序,通过main函数的args这个字符串数组解析出来。如同Scanner 对象一样,解析这个数组是按照空格区分的,因此,这个例子中我们得到的args数组是: | 数组元素 | 值 | 说明 | | ------- | --- | --- | | args[0] | 1 | | | args[1] | + | | | args[2] | 21 | | 后面的代码就很简单了。    ## 4. 本章重点 1. 掌握数组的申明和使用; 2. 掌握增强for循环的使用; 3. 理解数组作为函数参数的使用; 4. 其他内容均为了解。