数组
数组
数组是一种容器对象,用于存储固定数量的单一类型值。数组的长度在创建时确定,创建后长度固定不变。你在”Hello World!”应用程序的主方法中已经见过数组的示例。本节将更详细地讨论数组。
数组中的每个项称为元素,通过其数字索引访问每个元素。如前图所示,编号从0开始。例如,第6个元素的索引为5。
以下程序 ArrayDemo 创建一个整型数组,向数组中赋值,并将每个值打印到标准输出。
public class ArrayDemo {
public static void main(String[] args) {
// declares an array of integers
int[] anArray;
// allocates memory for 10 integers
anArray = new int[10];
// initialize the first element
anArray[0] = 100;
//initialize the second element
anArray[1] = 200;
// and so forth
anArray[2] = 300;
anArray[3] = 400;
anArray[4] = 500;
anArray[5] = 600;
anArray[6] = 700;
anArray[7] = 800;
anArray[8] = 900;
anArray[9] = 1000;
IO.println("Element at index 0: " + anArray[0]);
IO.println("Element at index 1: " + anArray[1]);
IO.println("Element at index 2: " + anArray[2]);
IO.println("Element at index 3: " + anArray[3]);
IO.println("Element at index 4: " + anArray[4]);
IO.println("Element at index 5: " + anArray[5]);
IO.println("Element at index 6: " + anArray[6]);
IO.println("Element at index 7: " + anArray[7]);
IO.println("Element at index 8: " + anArray[8]);
IO.println("Element at index 9: " + anArray[9]);
}
}
这个程序的输出如下所示:
Element at index 0: 100
Element at index 1: 200
Element at index 2: 300
Element at index 3: 400
Element at index 4: 500
Element at index 5: 600
Element at index 6: 700
Element at index 7: 800
Element at index 8: 900
Element at index 9: 1000
在实际编程场景中,您通常会使用支持的循环结构遍历数组中的每个元素,而非像前例那样逐行编写代码。不过该示例清晰地展示了数组语法。您将在控制流章节中学习各种循环结构(for、while 和 do-while)。
声明变量以引用数组
前面的程序通过以下代码声明了一个名为 anArray 的数组:
// declares an array of integers
int[] anArray;
与其他类型变量的声明类似,数组声明包含两个组成部分:数组类型和数组名称。数组类型写为 type[],其中 type 是包含元素的数据类型;方括号是特殊符号,表示该变量持有数组。数组的大小不属于其类型的一部分(因此方括号为空)。数组名称可自由命名,但须遵循”类”章节所述的命名规则与约定。与其他类型变量相同,声明本身不会实际创建数组,仅告知编译器该变量将存储指定类型的数组。
同样地,你可以声明其他类型的数组:
byte[] anArrayOfBytes;
short[] anArrayOfShorts;
long[] anArrayOfLongs;
float[] anArrayOfFloats;
double[] anArrayOfDoubles;
boolean[] anArrayOfBooleans;
char[] anArrayOfChars;
String[] anArrayOfStrings;
你也可以把括号放在数组名称之后:
// this form is discouraged
float anArrayOfFloats[];
然而,惯例不鼓励这种形式;括号用于标识数组类型,应与类型标识符一同出现。
创建、初始化和访问一个数组
创建数组的一种方式是使用new操作符。ArrayDemo程序中的下一条语句分配了一个能容纳10个整数元素的数组,并将该数组赋值给变量anArray。
// create an array of integers
anArray = new int[10];
如果缺少此语句,编译器将输出类似以下的错误信息,编译失败:
ArrayDemo.java:4: Variable anArray may not have been initialized.
接下来几行代码为数组的每个元素赋值:
anArray[0] = 100; // initialize the first element
anArray[1] = 200; // initialize the second element
anArray[2] = 300; // initialize the third element
每个数组元素通过其数字索引(numerical index 为什么中文要叫下标?)进行访问:
IO.println("Element at index 0: " + anArray[0]);
IO.println("Element at index 1: " + anArray[1]);
IO.println("Element at index 2: " + anArray[2]);
或者,你可以使用快捷语法来创建并初始化数组:
int[] anArray = {
100, 200, 300,
400, 500, 600,
700, 800, 900, 1000
}
此处数组的长度由大括号内以逗号分隔的值的数量决定。
创建多维数组
由于数组可以存储任何引用,且数组本身就是一种引用,因此可以轻松创建数组的数组。数组的数组也称为多维数组。可通过使用两个或多个方括号组来声明它们,例如 String[][] names。因此,每个元素都必须通过对应数量的索引值来访问。
在Java编程语言中,多维数组是指其元素本身也是数组的数组。这与C或Fortran中的数组不同——在这些语言中,二维数组是内存中连续的区域,可通过指针直接访问。Java中的数组虽也是内存中连续的区域,但由于二维数组本质上是引用数组,因此其本身并非连续的内存区域。由此产生的结果是,行长度可以变化,如下面的MultiDimArrayDemo程序所示:
public class MultiDimArrayDemo {
public static void main(String[] args) {
String[][] names = {
{"Mr. ", "Mrs. ", "Ms. "},
{"Smith", "Jones"}
};
// Mr.Smith
IO.println(names[0][0] + names[1][0]);
// Ms.Jones
IO.println(names[0][2] + names[1][1]);
}
}
这个程序的输出如下所示
Mr. Smith
Ms. Jones
这些数组有时候被称为不规则数组,或者锯齿形数组。
##使用数组的长度属性
最后,您可以使用任何数组上定义的内置 length 属性来确定该数组的大小。以下代码将数组的大小打印到标准输出:
IO.println(anArray.length);
这对于嵌套数组尤其有用,其中每个数组的长度可能不同。您的代码不应假设所有这些数组长度相同,而应依赖此长度属性,如下面的代码所示:
void displayBidimensionalArray(String[][] strings){
for (int arrayIndex = 0; arrayIndex < strings.length; arrayIndex++) {
for (int index = 0; index < strings[arrayIndex].length; index++) {
IO.print(strings[arrayIndex][index]+ " ");
}
IO.println();
}
}
你可以使用以下数组调用此方法。
String[][] strings = {
{"one"},
{"Maria", "Jennifer", "Particia"},
{"James", "Michael"},
{"Washington", "London", "Paris", "Berlin", "Tokyo"}
};
displayBidimensionalArray(strings);
代码的输出如下所示:
one
Maria Jennifer Particia
James Michael
Washington London Paris Berlin Tokyo
数组的拷贝
System类提供了一个arraycopy()方法,可用于高效地将数据从一个数组复制到另一个数组:
public static void arraycopy(Object src, int srcPos,
Object dest, int destPos, int length)
两个对象参数分别指定要复制的源数组和目标数组。三个整数参数分别指定源数组的起始位置、目标数组的起始位置以及要复制的数组元素数量。
以下程序 ArrayCopyDemo 声明了一个字符串元素数组。它使用 System.arraycopy() 方法将数组中部分元素复制到第二个数组:
public class ArrayCopyDemo {
public static void main(String[] args) {
String[] copyFrom = {
"Affogato", "Americano", "Cappuccino", "Corretto", "Cortado",
"Doppio", "Espresso", "Frappucino", "Freddo", "Lungo", "Macchiato",
"Marocchino", "Ristretto"};
String[] copyTo = new String[7];
System.arraycopy(copyFrom, 2, copyTo, 0, 7);
for (String coffee : copyTo) {
IO.print(coffee + " ");
}
IO.println();
}
}
代码输出如下所示:
Cappuccino Corretto Cortado Doppio Espresso Frappucino Freddo
数组的操作
数组是编程中一种强大且实用的概念。Java SE 提供了执行数组相关常见操作的方法。例如,ArrayCopyDemo 示例使用 System 类的 arraycopy() 方法,而非手动遍历源数组的元素并将每个元素逐个放入目标数组。该操作在后台自动完成,开发者只需一行代码即可调用该方法。
为方便起见,Java SE 在 Arrays 类中提供了多种方法来执行数组操作(如复制、排序和搜索数组等常见任务)。例如,前面的示例可修改为使用 Arrays 类的 Arrays 方法,具体可参见 ArrayCopyOfDemo 示例。其区别在于:使用 Arrays 方法时无需在调用前创建目标数组,因为该方法会直接返回目标数组:
public class ArrayCopyOfDemo {
public static void main(String[] args) {
String[] copyFrom = {
"Affogato", "Americano", "Cappuccino", "Corretto", "Cortado",
"Doppio", "Espresso", "Frappucino", "Freddo", "Lungo", "Macchiato",
"Marocchino", "Ristretto"};
String[] copyTo = Arrays.copyOfRange(copyFrom, 2, 9);
for (String coffee : copyTo) {
IO.print(coffee + " ");
}
IO.println();
}
}
如您所见,该程序的输出结果相同,不过所需代码行数更少。请注意,Arrays方法的第二个参数是待复制范围的起始索引(包含该索引),而第三个参数是待复制范围的结束索引(不包含该索引)。在此示例中,待复制的范围不包含索引为9的数组元素(该元素包含字符串 Lungo)。
数组类方法提供的其他有用操作包括:
- 在数组中搜索特定值以获取其所在的索引位置(the binarySearch() method)。
- 比较两个数组以确定它们是否相等(equals()方法)。
- 填充数组以在每个索引处放置特定值(fill()方法)。
- 将数组按升序排序。这既可通过顺序方式使用 sort() 方法实现,也可通过 Java SE 8 引入的 parallelSort() 方法并行完成。在多处理器系统上,对大型数组进行并行排序的速度快于顺序排序。
- 创建一个以数组作为源的流(使用 stream() 方法)。例如,以下语句以与前例相同的方式打印 copyTo 数组的内容:
Arrays.stream(copyTo).map(coffee -> coffee + " ").forEach(IO::print);
有关流的更多信息,请参阅聚合操作。
- 将数组转换为字符串。toString()方法将数组的每个元素转换为字符串,用逗号分隔,然后用方括号包围。例如,以下语句将 copyTo 数组转换为字符串并打印出来:
IO.println(Arrays.toString(copyTo));
该语句的输出如下所示:
[Cappuccino, Corretto, Cortado, Doppio, Espresso, Frappucino, Freddo]
变量与数组的封装
Java编程语言在其术语体系中同时使用”字段”和”变量”的概念。实例变量(非静态字段)是类每个实例专属的。类变量(静态字段)是通过static修饰符声明的字段;无论类被实例化多少次,类变量都仅存在一份副本。局部变量用于存储方法内部的临时状态。参数则是为方法提供额外信息的变量;局部变量和参数均始终被归类为”变量”(而非”字段”)。为字段或变量命名时,存在必须遵循的命名规则与约定。
八种基本数据类型为:byte、short、int、long、float、double、boolean 和 char。java.lang.String 类表示字符串。编译器会为上述类型的字段分配合理的默认值;对于局部变量,则永远不会分配默认值。
字面量是固定值在源代码中的表示形式。数组是一种容器对象,用于存储固定数量的单一类型值。数组的长度在创建时确定,创建后其长度固定不变。