数组操作
约 2297 字大约 8 分钟
2025-07-13
本节我们将讲解对数组的操作,包括:
- 遍历;
- 排序。
以及多维数组的概念。
5.1 数组遍历
在 Java 程序中,数组是一种重要的数据类型。为了操作数组,最常见的操作之一就是遍历。
5.1.1 for
循环遍历
通过 for
循环可以遍历数组。由于数组的每个元素都可以通过索引来访问,因此,可以使用标准的 for
循环完成数组的遍历:
// 遍历数组
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int i=0; i<ns.length; i++) {
int n = ns[i];
System.out.println(n);
}
}
}
这段代码展示了如何使用 for
循环遍历数组 ns
。初始条件 i=0
表示从数组的第一个元素开始遍历,循环条件 i<ns.length
确保不会超出数组的索引范围,每次循环后 i++
用于访问下一个元素。
5.1.2 for each
循环
第二种方式是使用 for each
循环,它可以直接迭代数组的每个元素:
// 遍历数组
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}
}
}
在 for (int n : ns)
循环中,变量 n
直接获取 ns
数组的元素,而不是索引。for each
循环更加简洁,但无法获取数组的索引。选择哪种 for
循环取决于实际需求。
5.1.3 打印数组内容
直接打印数组变量,得到的是数组在 JVM 中的引用地址,例如 [I@7852e922
。这并没有实际意义,因为我们希望打印数组的元素内容。虽然可以使用 for each
循环来打印数组内容,但比较繁琐。Java 标准库提供了 Arrays.toString()
方法,可以快速打印数组内容:
// 遍历数组
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 1, 1, 2, 3, 5, 8 };
System.out.println(Arrays.toString(ns));
}
}
这段代码首先导入 java.util.Arrays
类,然后使用 Arrays.toString(ns)
方法将数组 ns
转换为字符串并打印出来。这比使用循环手动拼接字符串更加方便快捷。Arrays.toString()
方法是数组操作中常用的工具,能够方便地查看数组的内容。
5.2 数组排序
数组排序是编程中的常见需求,本节将介绍常用的排序算法以及 Java 标准库提供的排序方法。
5.2.1 冒泡排序
冒泡排序是一种简单的排序算法。它重复地遍历要排序的数组,比较相邻的两个元素,并且按照从小到大的顺序交换它们。每一轮遍历都会将当前未排序部分的最大值移动到数组的末尾。
以下是使用冒泡排序算法对一个整型数组从小到大进行排序的 Java 代码示例:
// 冒泡排序
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
// 排序前:
System.out.println(Arrays.toString(ns));
for (int i = 0; i < ns.length - 1; i++) {
for (int j = 0; j < ns.length - i - 1; j++) {
if (ns[j] > ns[j+1]) {
// 交换ns[j]和ns[j+1]:
int tmp = ns[j];
ns[j] = ns[j+1];
ns[j+1] = tmp;
}
}
}
// 排序后:
System.out.println(Arrays.toString(ns));
}
}
在这段代码中,外层循环控制遍历的轮数,内层循环负责比较和交换相邻元素。需要注意的是,交换两个变量的值时,必须借助一个临时变量,以避免数据覆盖。
5.2.2 使用 Arrays.sort()
排序
实际上,Java 的标准库已经内置了排序功能,通过调用 Arrays.sort()
方法即可实现数组排序。
// 排序
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
Arrays.sort(ns);
System.out.println(Arrays.toString(ns));
}
}
Arrays.sort()
方法使用了优化的排序算法,通常比冒泡排序等简单算法的效率更高。
5.2.3 数组排序的内存模型
需要注意的是,对数组进行排序会直接修改数组本身。例如,对于整型数组,排序会改变数组中元素的顺序。而对于字符串数组,排序会改变数组中每个元素指向的字符串的引用,但不会改变字符串本身。
以下是字符串数组排序的内存模型示例:
┌──────────────────────────────────┐
┌───┼──────────────────────┐ │
│ │ ▼ ▼
┌───┬─┴─┬─┴─┬───┬────────┬───┬───────┬───┬──────┬───┐
ns ─────▶│░░░│░░░│░░░│ │"banana"│ │"apple"│ │"pear"│ │
└─┬─┴───┴───┴───┴────────┴───┴───────┴───┴──────┴───┘
│ ▲
└─────────────────┘
排序前,字符串数组在内存中,数组的每个元素存储的是字符串的引用地址。
┌──────────────────────────────────┐
┌───┼──────────┐ │
│ │ ▼ ▼
┌───┬─┴─┬─┴─┬───┬────────┬───┬───────┬───┬──────┬───┐
ns ─────▶│░░░│░░░│░░░│ │"banana"│ │"apple"│ │"pear"│ │
└─┬─┴───┴───┴───┴────────┴───┴───────┴───┴──────┴───┘
│ ▲
└──────────────────────────────┘
排序后,数组中元素的引用地址发生了改变,指向了不同的字符串,但字符串对象本身在内存中并没有发生改变。
Arrays.sort(ns)
: 这行代码调用 Arrays.sort()
方法对数组 ns
进行排序。这个方法直接修改了数组 ns
的内容,将其元素按照升序排列。 理解这一点非常重要,因为这意味着排序操作会改变原始数组,而不是创建一个新的排序后的数组。
5.3 多维数组
5.3.1 二维数组
二维数组本质上是数组的数组,可以理解为一种特殊的一维数组,其每个元素又是一个一维数组。
在 Java 中,定义二维数组的语法如下:
// 二维数组
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
System.out.println(ns.length); // 3
}
}
上述代码定义了一个名为 ns
的二维数组,它包含了 3 个一维数组,每个一维数组又包含了若干个整数。ns.length
的值为 3
,表示 ns
包含 3 个数组。
这个二维数组在内存中的结构如下:
┌───┬───┬───┬───┐
┌───┐ ┌──▶│ 1 │ 2 │ 3 │ 4 │
ns ─────▶│░░░│──┘ └───┴───┴───┴───┘
├───┤ ┌───┬───┬───┬───┐
│░░░│─────▶│ 5 │ 6 │ 7 │ 8 │
├───┤ └───┴───┴───┴───┘
│░░░│──┐ ┌───┬───┬───┬───┐
└───┘ └──▶│ 9 │10 │11 │12 │
└───┴───┴───┴───┘
下段代码展示了如何获取二维数组中的一个一维数组:
// 二维数组
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 }
};
int[] arr0 = ns[0];
System.out.println(arr0.length); // 4
}
}
ns[0]
获取了 ns
数组的第 0 个元素,即 arr0
指向了数组 { 1, 2, 3, 4 }
。arr0.length
的值为 4,表示 arr0
指向的数组包含 4 个元素。
访问二维数组的元素使用 array[row][col]
的形式,其中 row
是行索引,col
是列索引。例如:
System.out.println(ns[1][2]); // 7
二维数组的每个数组元素的长度可以不相同。例如:
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6 },
{ 7, 8, 9 }
};
这个二维数组中,每个一维数组的长度各不相同。其内存中结构示意图如下:
┌───┬───┬───┬───┐
┌───┐ ┌──▶│ 1 │ 2 │ 3 │ 4 │
ns ─────▶│░░░│──┘ └───┴───┴───┴───┘
├───┤ ┌───┬───┐
│░░░│─────▶│ 5 │ 6 │
├───┤ └───┴───┘
│░░░│──┐ ┌───┬───┬───┐
└───┘ └──▶│ 7 │ 8 │ 9 │
└───┴───┴───┘
打印二维数组可以使用两层嵌套的 for
循环:
for (int[] arr : ns) {
for (int n : arr) {
System.out.print(n);
System.out.print(', ');
}
System.out.println();
}
也可以使用 Java 标准库的 Arrays.deepToString()
方法:
// 二维数组
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[][] ns = {
{ 1, 2, 3, 4 },
{ 5, 6 },
{ 7, 8, 9 }
};
System.out.println(Arrays.deepToString(ns));
}
}
Arrays.deepToString()
方法能够方便地将二维数组转换为字符串进行打印。
5.3.2 三维数组
在理解了二维数组的基础上,可以很容易地理解三维数组。三维数组就是二维数组的数组。
定义一个三维数组的示例代码如下:
int[][][] ns = {
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
},
{
{10, 11},
{12, 13}
},
{
{14, 15, 16},
{17, 18}
}
};
其在内存中的结构示意图如下:
┌───┬───┬───┐
┌───┐ ┌──▶│ 1 │ 2 │ 3 │
┌──▶│░░░│──┘ └───┴───┴───┘
│ ├───┤ ┌───┬───┬───┐
│ │░░░│─────▶│ 4 │ 5 │ 6 │
│ ├───┤ └───┴───┴───┘
│ │░░░│──┐ ┌───┬───┬───┐
┌───┐ │ └───┘ └──▶│ 7 │ 8 │ 9 │
ns ────▶│░░░│──┘ └───┴───┴───┘
├───┤ ┌───┐ ┌───┬───┐
│░░░│─────▶│░░░│─────▶│10 │11 │
├───┤ ├───┤ └───┴───┘
│░░░│──┐ │░░░│──┐ ┌───┬───┐
└───┘ │ └───┘ └──▶│12 │13 │
│ └───┴───┘
│ ┌───┐ ┌───┬───┬───┐
└──▶│░░░│─────▶│14 │15 │16 │
├───┤ └───┴───┴───┘
│░░░│──┐ ┌───┬───┐
└───┘ └──▶│17 │18 │
└───┴───┘
访问三维数组的元素需要使用三个索引,例如,ns[2][0][1]
,按照索引的顺序,可以定位到最终的元素 15
。
5.4 命令行参数
Java 程序的入口是 main
方法,该方法可以接收一个命令行参数,其类型为 String[]
数组。这个数组用于接收用户在命令行中输入的参数,并由 Java 虚拟机 (JVM) 传递给 main
方法。
public class Main {
public static void main(String[] args) {
for (String arg : args) {
System.out.println(arg);
}
}
}
上述代码展示了如何访问和打印传递给 main
方法的命令行参数。args
数组包含了所有在命令行中输入的参数,程序通过循环遍历该数组,并将每个参数打印到控制台。
我们可以根据接收到的命令行参数,执行不同的代码逻辑。例如,实现一个 -version
参数,用于打印程序的版本号:
public class Main {
public static void main(String[] args) {
for (String arg : args) {
if ("-version".equals(arg)) {
System.out.println("v 1.0");
break;
}
}
}
}
上述代码演示了如何解析命令行参数。程序通过循环遍历 args
数组,并使用 String.equals()
方法比较每个参数是否为 "-version"。如果找到该参数,则打印程序的版本号 "v 1.0" 并使用 break
语句退出循环。
要运行带有命令行参数的 Java 程序,需要先使用 javac
命令编译源代码:
javac Main.java
然后,在执行程序时,通过 java
命令传递参数:
java Main -version
在这个例子中,java Main -version
命令告诉 JVM 运行 Main
类,并将 "-version" 作为命令行参数传递给 main
方法。程序根据传入的命令行参数,执行相应的操作,即打印版本号。