Files
Java-Book/02:Java语言基础/06.md
T

357 lines
11 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
数组的定义和使用大多和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是运行Java程序的命令,后面的 `Calculator` 是Java编译后的可执行代码;`1 + 21` 是命令行参数。这个参数会传递给Calculator的主程序,通过main函数的args这个字符串数组解析出来。如同Scanner 对象一样,解析这个数组是按照空格区分的,因此,这个例子中我们得到的args数组是:
| 数组元素 | 值 | 说明 |
| ------- | --- | --- |
| args[0] | 1 | |
| args[1] | + | |
| args[2] | 21 | |
后面的代码就很简单了。
  
## 4. 本章重点
1. 掌握数组的声明和使用;
2. 掌握增强for循环的使用;
3. 理解数组作为函数参数的使用;
4. 其他内容均为了解。