
1. 引言
前几篇已经搞定环境、变量和流程控制,这篇来说数组。为什么需要它?举个例子:班上有 50 个同学,想把每个人的成绩都存下来,如果还是一个个声明变量,就得写 score1 到 score50,再往后 100 个、1000 个怎么办?这种时候变量明显不够用了。
数组就是来解决这类问题的:一个变量就能装下一批同类型的数据。下面从声明、初始化、遍历,再到几个常用操作,逐个过一遍。
2. 什么是数组
数组可以理解成一批同类型数据的容器。拿储物柜类比:柜子规格都一样,每个柜子有唯一的编号,按编号就能找到对应的东西。
它有几个特点:
- 类型固定 :一个数组里只能装同一种类型的数据,要么全是
int,要么全是String。 - 长度固定 :创建时多长就是多长,后面改不了;想动态扩容,得用
ArrayList这类集合。 - 索引从 0 开始 :第一个元素的下标是
0,最后一个是长度 - 1。 - 内存连续:元素在内存里挨着放,所以按下标访问速度很快。
3. 数组的声明与初始化
用数组分两步:先声明,再初始化(分配空间并赋值)。
3.1 声明数组
声明有两种写法,推荐第一种:
java
// 推荐写法:数据类型[] 数组名;
int[] scores;
String[] names;
// 兼容 C/C++ 风格的写法:数据类型 数组名[];
int scores2[];
3.2 初始化数组
初始化有三种方式。
方式一:动态初始化------只指定长度,元素使用默认值
java
int[] scores = new int[5]; // 创建一个长度为 5 的 int 数组
此时数组中的 5 个元素会被自动赋予默认值。不同类型的默认值如下:
| 数据类型 | 默认值 |
|---|---|
int / short / byte / long |
0 |
double / float |
0.0 |
char |
'\\u0000'(空字符) |
boolean |
false |
引用类型(如 String) |
null |
方式二:静态初始化------声明的同时直接赋值
java
int[] scores = {88, 92, 75, 100, 60};
String[] names = {"张三", "李四", "王五"};
方式三:先声明,后静态初始化(注意:此时不能省略 new)
java
int[] scores;
scores = new int[]{88, 92, 75, 100, 60};
下面这种写法会编译报错,因为只有声明的同时才能省略 new int[]:
java
int[] scores;
scores = {88, 92, 75}; // 编译错误!
4. 访问与修改数组元素
数组通过索引访问,写法是 数组名[索引]。
java
public class ArrayAccessDemo {
public static void main(String[] args) {
int[] scores = {88, 92, 75, 100, 60};
// 读取第一个元素(索引为 0)
System.out.println("第一名同学的成绩:" + scores[0]);
// 修改第三个元素(索引为 2)
scores[2] = 80;
System.out.println("修改后第三名同学的成绩:" + scores[2]);
// 获取数组长度
System.out.println("数组长度:" + scores.length);
}
}
运行结果:
text
第一名同学的成绩:88
修改后第三名同学的成绩:80
数组长度:5
提示:
数组名.length用来获取数组长度,它是属性不是方法,后面不要加括号。
4.1 数组越界异常
如果索引跑到 0 ~ length-1 范围之外,就会抛 ArrayIndexOutOfBoundsException:
java
int[] scores = {88, 92, 75};
System.out.println(scores[3]); // 运行时报错:索引越界
这个错误新手经常踩,取值前记得先确认索引范围。
5. 遍历数组
遍历就是把数组里的元素一个个取出来。上一篇文章学的循环这里都用得上,常见有三种写法。
5.1 使用普通 for 循环
java
public class ArrayTraversalDemo {
public static void main(String[] args) {
int[] scores = {88, 92, 75, 100, 60};
for (int i = 0; i < scores.length; i++) {
System.out.println("第 " + (i + 1) + " 名同学的成绩:" + scores[i]);
}
}
}
5.2 使用增强 for 循环(for-each)
Java 5 之后多了增强 for 循环,写起来更省事,适合只读遍历:
java
for (int score : scores) {
System.out.println("成绩:" + score);
}
注意,这里拿到的 score 只是副本,改它不会影响原数组:
java
for (int score : scores) {
score = 0; // 只修改了副本,原数组不变
}
System.out.println(scores[0]); // 仍然输出 88
如果要在遍历时改元素,还是用普通 for 循环带下标比较靠谱。
6. 数组的常见操作
下面这几个是平时写数组最常碰到的处理逻辑,都敲一遍以后就熟了。
6.1 求最值
java
public class ArrayMaxDemo {
public static void main(String[] args) {
int[] scores = {88, 92, 75, 100, 60};
int max = scores[0]; // 假设第一个元素是最大值
for (int i = 1; i < scores.length; i++) {
if (scores[i] > max) {
max = scores[i];
}
}
System.out.println("最高分:" + max);
}
}
6.2 求和与平均值
java
public class ArrayAverageDemo {
public static void main(String[] args) {
int[] scores = {88, 92, 75, 100, 60};
int sum = 0;
for (int score : scores) {
sum += score;
}
double average = (double) sum / scores.length;
System.out.println("总分:" + sum);
System.out.printf("平均分:%.2f%n", average);
}
}
6.3 数组反转
java
public class ArrayReverseDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
for (int left = 0, right = arr.length - 1; left < right; left++, right--) {
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
// 输出反转结果
for (int num : arr) {
System.out.print(num + " ");
}
}
}
运行结果:
text
5 4 3 2 1
6.4 冒泡排序
排序在面试和比赛里经常碰到,先看最经典的冒泡排序:
java
public class BubbleSortDemo {
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.print("排序结果:");
for (int num : arr) {
System.out.print(num + " ");
}
}
}
运行结果:
text
排序结果:11 12 22 25 34 64 90
7. 二维数组
前面的数组都是一维的,像一条线。二维数组可以看成"数组的数组",更像一张表格,有行有列。
7.1 二维数组的初始化
java
// 静态初始化:3 行 3 列的矩阵
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// 动态初始化:3 行 4 列,每个元素默认值为 0
int[][] grid = new int[3][4];
7.2 遍历二维数组
java
public class TwoDimensionalArrayDemo {
public static void main(String[] args) {
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println(); // 每行输出后换行
}
}
}
运行结果:
text
1 2 3
4 5 6
7 8 9
补充:Java 的二维数组每一行列数可以不一样,这叫"不规则数组"。比如
int[][] triangle = {``{1}, {2, 3}, {4, 5, 6}};就是个三角形结构。
8. 实战练习
做完下面几题,这篇的内容基本就上手了:
- 成绩统计 :创建一个长度为 10 的
int数组,随机存入 0~100 之间的成绩,统计最高分、最低分和平均分。 - 元素查找 :在数组
{12, 45, 67, 89, 34, 23}中查找某个数字是否存在,若存在则输出其索引。 - 数组去重 :给定数组
{1, 3, 2, 3, 5, 1, 4},输出其中不重复的元素。 - 矩阵对角线求和:给定一个 3×3 的二维数组,计算主对角线(左上到右下)元素之和。
9. 数组的局限与下一步
数组最大的问题是长度写死。程序跑起来以后想随时增删元素,它就不太好使;而且像插入、删除、查找这些常用操作,都得自己写。
集合框架里的 ArrayList、HashMap 解决了这些问题:支持动态扩容,还自带不少方法。不过数组是更底层的东西,先把数组弄明白,后面学集合和算法会轻松很多。
下一篇看 Java 里的方法(函数),把重复的代码封装起来。
10. 总结
这篇的内容,一句话概括:数组用来存一批同类型数据,长度固定、索引从 0 开始;初始化分动态和静态;访问用 数组名[索引],长度用 length;遍历时普通 for 适合边改边读,增强 for 适合只读;常用操作练了最值、求和、反转、冒泡排序;二维数组就是"数组的数组"。
原理本身不难,关键还是动手敲。练习没做完的话回去补上,比只看一遍有用得多。