Java 中的数组
数组的概念
数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。
数组中的概念:
- 数组名
- 下标(或索引,或偏移量)
- 元素
- 数组的长度
数组的特点
- 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
- 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。
- 数组中的元素在内存中是依次紧密排列的,有序的。
- 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改。
- 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
- 数组名中引用的是这块连续空间的首地址。
数组的分类
1、按照元素类型分:
- 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
- 引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)
2、按照维度分:
- 一维数组:存储一组数据
- 二维数组:存储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样
一维数组的使用
一维数组的声明
格式:
// 推荐
元素的数据类型[] 一维数组的名称;
// 不推荐
元素的数据类型 一维数组名称[];举例:
int[] arr;
int arr1[];
double[] arr2;
String[] arr3; // 引用类型变量数组数组的声明,需要明确:
-(1)数组的维度:在 Java 中数组的符号是 [],[] 表示一维,[][] 表示二维。 -(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的 Java 的数据类型。例如:int、String、Student 等。 -(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。
举例:
public class ArrayTest1 {
public static void main(String[] args) {
// 比如,要存储一个小组的成绩
int[] scores;
int grades[];
// System.out.println(scores); // 未初始化不能使用
// 比如,要存储一组字母
char[] letters;
// 比如,要存储一组姓名
String[] names;
// 比如,要存储一组价格
double[] prices;
}
}注意
Java 语言中声明数组时不能指定其长度(数组中元素的个数)。例如 int a[5]; 是非法的。
一维数组的初始化
静态初始化
- 如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。这里的“静态”指的是“静态数据”(在编译时就已经知道了)。
- 静态初始化的数组长度由静态数据的个数决定。
一维数组声明和静态初始化格式 1:
数据类型[] 数组名 = new 数据类型[]{ 元素 1, 元素 2, 元素 3, ...};
或
数据类型[] 数组名;
数组名 = new 数据类型[]{ 元素 1, 元素 2, 元素 3, ...};
new是关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用 new 创建数组实体。
例如,定义存储 1,2,3,4,5 整数的数组容器。
int[] arr = new int[]{1, 2, 3, 4, 5}; // 正确
// 或
int[] arr;
arr = new int[]{1, 2, 3, 4, 5}; // 正确一维数组声明和静态初始化格式 2:
省略了 new 数据类型[],但是必须在一个语句中完成,不能分成两个语句写。
数据类型[] 数组名 = {元素 1, 元素 2, 元素 3...};例如,定义存储 1,2,3,4,5 整数的数组容器:
int[] arr = {1, 2, 3, 4, 5}; // 正确
int[] arr;
arr = {1, 2, 3, 4, 5}; // 错误举例:
public class ArrayTest2 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5}; // 右边不需要写 new int[]
int[] nums;
nums = new int[]{10,20,30,40}; // 声明和初始化在两个语句完成,就不能不使用 new int[]
char[] word = {'h','e','l','l','o'};
String[] heros = {"袁隆平","邓稼先","钱学森"};
System.out.println("arr 数组:" + arr);// arr 数组:[I@1b6d3586
System.out.println("nums 数组:" + nums);// nums 数组:[I@4554617c
System.out.println("word 数组:" + word);// word 数组:[C@74a14482
System.out.println("heros 数组:" + heros);// heros 数组:[Ljava.lang.String;@1540e19d
}
}动态初始化
只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值,称为动态初始化。
格式:
数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度];
或
数组存储的数据类型[] 数组名字;
数组名字 = new 数组存储的数据类型[长度];
- [长度]:数组的长度,表示数组容器中可以最多存储多少个元素。
- 数组有定长特性,长度一旦指定,不可更改。
举例 1:正确写法
int[] arr = new int[5];
int[] arr;
arr = new int[5];举例 2:错误写法 花括号里 {} 指定了元素列表,就不需要在 [] 中指定元素个数了。
int[] arr = new int[5]{1, 2, 3, 4, 5}; // 错误。数组的长度
- 数组的元素总个数,即数组的长度
- 每个数组都有一个属性 length 指明它的长度,例如:arr.length 指明数组 arr 的长度(即元素个数)
- 每个数组都具有长度,而且一旦初始化,其长度就是确定,且是不可变的
数组元素的引用
如何表示数组中的一个元素? 每一个存储到数组的元素,都会自动的拥有一个编号,从 0 开始,这个自动编号称为数组索引(index)或下标或偏移量,可以通过数组的索引/下标访问到数组中的元素。
数组名[索引/下标]数组的下标范围 Java 中数组的下标从 0 开始,下标范围是 [0, 数组的长度 - 1],即 [0, 数组名.length - 1]。
数组元素下标可以是整型常量或整型表达式。如 a[3]、b[i]、c[6*i]。
一维数组的遍历
将数组中的每个元素分别获取出来,就是遍历。for 循环与数组的遍历是绝配。
举例 1:
public class ArrayTest4 {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5};
// 打印数组的属性,输出结果是 5
System.out.println("数组的长度:" + arr.length);
// 遍历输出数组中的元素
System.out.println("数组的元素有:");
for(int i = 0; i < arr.length; i++){
System.out.println(arr[i]);
}
}
}举例 2:
public class ArrayTest5 {
public static void main(String[] args) {
int[] arr = new int[5];
System.out.println("arr 数组的长度:" + arr.length);
System.out.print("存储数据到 arr 数组之前:[");
for (int i = 0; i < arr.length; i++) {
if (i == 0){
System.out.print(arr[i]);
} else {
System.out.print(", " + arr[i]);
}
}
System.out.println("]");
// 初始化
/*
arr[0] = 2;
arr[1] = 4;
arr[2] = 6;
arr[3] = 8;
arr[4] = 10;
*/
for (int i = 0; i < arr.length; i++) {
arr[i] = (i+1) * 2;
}
System.out.print("存储数据到 arr 数组之后:[");
for (int i = 0; i < arr.length; i++) {
if(i==0){
System.out.print(arr[i]);
}else{
System.out.print("," + arr[i]);
}
}
System.out.println("]");
}
}数组元素的默认值
数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。例如:
public class ArrayTest6 {
public static void main(String argv[]){
int a[]= new int[5];
System.out.println(a[3]); // a[3] 的默认值为 0
}
}对于基本数据类型而言,默认初始化值各有不同。
对于引用数据类型而言,默认初始化值为 null,注意与 0 不同!
| 数组元素的类型 | 元素的默认初始值 |
|---|---|
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0L |
| float | 0.0f |
| double | 0.0 |
| char | 0 或写为 '\u0000'(显示为空) |
| boolean | false |
| 引用类型 | null |
public class ArrayTest7 {
public static void main(String[] args) {
//存储 26 个字母
char[] letters = new char[26];
System.out.println("letters 数组的长度:" + letters.length);
System.out.print("存储字母到 letters 数组之前:[");
for (int i = 0; i < letters.length; i++) {
if(i==0){
System.out.print(letters[i]);
}else{
System.out.print("," + letters[i]);
}
}
System.out.println("]");
// 存储 5 个姓名
String[] names = new String[5];
System.out.println("names 数组的长度:" + names.length);
System.out.print("存储姓名到 names 数组之前:[");
for (int i = 0; i < names.length; i++) {
if(i==0){
System.out.print(names[i]);
}else{
System.out.print("," + names[i]);
}
}
System.out.println("]");
}
}一维数组内存分析
Java 虚拟机的内存划分
Java 为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

| 区域名称 | 作用 |
|---|---|
| 虚拟机栈 | 用于存储正在执行的每个 Java 方法的局部变量表等。局部变量表存放了编译器可知长度的各种基本数据类型、对象引用。方法执行完,自动释放。 |
| 堆内存 | 存储对象(包括数组对象),通过 new 创建的都存储在堆内存里。 |
| 方法区 | 存储已被虚拟机加载的类信息、常量、静态常量、即使编译器编译后的代码等数据。 |
| 本地方法栈 | 当程序中调用了 native 的本地方法时,本地方法执行期间的内存区域。 |
| 程序计数器 | 程序计数器是 CPU 中的寄存器,它包含每一个线程下一条要执行的指令的地址。 |
一维数组在内存中的存储
public static void main(String[] args) {
int[] arr = new int[3];
System.out.println(arr); // [I@5f150435
}
数组下标为什么是 0 开始
0 代表的是偏移量,就像直尺的刻度是从 0 开始度量。
一维数组的应用
案例 1,租房电话号码
升景坊单间短期出租 4 个月,550 元/月(水电煤公摊,网费 35 元/月),空调、卫生间、厨房齐全。屋内均是 IT 行业人士,喜欢安静。所以要求来租者最好是同行或者刚毕业的年轻人,爱干净、安静。电话号码如下:
public class ArrTest1 {
public static void main(String[] args) {
int[] arr = new int[]{1,9,1,2,2,5,4,4,4,6,2};
int[] index = new int[]{0,1,2,3,4,5,6,7,8,9,10};
String tel = "";
for(int i = 0;i < index.length;i++){
tel += arr[index[i]];
}
System.out.println("我的联系方式:" + tel);
}
}案例 2,输出英文星期几
用一个数组,保存星期一到星期天的 7 个英语单词,从键盘输入 1-7,显示对应的单词:
{"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}
import java.util.Scanner;
/**
* Description:
*/
public class WeekArrayTest {
public static void main(String[] args) {
// 1. 声明并初始化星期的数组
String[] weeks = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
// 2. 使用 Scanner 从键盘获取 1-7 范围的整数
Scanner scanner = new Scanner(System.in);
System.out.println("请输入 [1-7] 范围的整数:");
int number = scanner.nextInt();
if (number < 1 || number > 7) {
System.out.println("你输入的输入非法");
} else {
// 3. 根据输入的整数,到数组中相应的索引位置获取指定的元素(即:星期几)
System.out.println("对应的星期为:" + weeks[number - 1]);
}
scanner.close();
}
}案例 3,找出最高分
从键盘读入学生成绩,找出最高分,并输出学生成绩等级。
- 成绩 >=
最高分-10等级为’A’ - 成绩 >=
最高分-20等级为’B’ - 成绩 >=
最高分-30等级为’C’ - 其余等级为’D’
提示:先读入学生人数,根据人数创建 int 数组,存放学生成绩。
import java.util.Scanner;
/**
* Description:
*/
public class ScoreTest1 {
public static void main(String[] args) {
// 1. 根据提示,获取学生人数
System.out.print("请输入学生人数:");
Scanner scanner = new Scanner(System.in);
int count = scanner.nextInt();
// 2. 根据学生人数,创建指定长度的数组 (使用动态初始化)
int[] scores = new int[count];
// 3. 使用循环,依次给数组的元素赋值
int maxScore = 0; // 记录最高分
System.out.println("请输入 " + count + " 个成绩");
for (int i = 0; i < scores.length; i++) {
scores[i] = scanner.nextInt();
// 4. 获取数组中元素的最大值,即为最高分
if(maxScore < scores[i]){
maxScore = scores[i];
}
}
System.out.println("最高分是:" + maxScore);
// 5. 遍历数组元素,输出各自的分数,并根据其分数与最高分的差值,获取各自的等级
char grade;
for (int i = 0; i < scores.length; i++) {
if(scores[i] >= maxScore - 10){
grade = 'A';
}else if(scores[i] >= maxScore - 20){
grade = 'B';
}else if(scores[i] >= maxScore - 30){
grade = 'C';
}else{
grade = 'D';
}
System.out.println("student " + i + " socre is " + scores[i] + ", grade is " + grade);
}
// 关闭资源
scanner.close();
}
}多维数组的使用
Java 语言里提供了支持多维数组的语法。
应用举例1 :
某公司 2022 年全年各个月份的销售额进行登记。按月份存储,可以使用一维数组。如下:
int[] monthData = new int[]{23,43,22,34,55,65,44,67,45,78,67,66};如果改写为按季度为单位存储怎么办呢?
int[][] quarterData = new int[][]{{23,43,22},{34,55,65},{44,67,45},{78,67,66}};应用举例 2:
高一年级三个班级均由多个学生姓名构成一个个数组。如下:
String[] class1 = new String[]{"段誉","令狐冲","任我行"};
String[] class2 = new String[]{"张三丰","周芷若"};
String[] class3 = new String[]{"赵敏","张无忌","韦小宝","杨过"};那从整个年级看,我们可以声明一个二维数组。如下:
String[][] grade = new String[][]{{"段誉","令狐冲","任我行"},{"张三丰","周芷若"},{"赵敏","张无忌","韦小宝","杨过"}};声明
二维数组声明的语法格式:
// 推荐
子数组元素的数据类型[][] 二维数组的名称;
// 不推荐
子数组元素的数据类型 二维数组名[][];
// 不推荐
子数组元素的数据类型[] 二维数组名[];例如:
public class Test20TwoDimensionalArrayDefine {
public static void main(String[] args) {
//存储多组成绩
int[][] grades;
//存储多组姓名
String[][] names;
}
}int[] x, y[];
// x 是一维数组,y 是二维数组静态初始化
格式:
int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};动态初始化
如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:
格式 1:规则二维表:父数组的长度和子数组的长度相同
// 声明并动态初始化
元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n];
// 赋值
二维数组名[行下标][列下标] = 值;格式 2:不规则:各个子数组的长度不一样
//(1)先确定父数组的长度
子数组元素的数据类型[][] 二维数组名 = new 子数组元素的数据类型[父数组长度][];
// 此时只是确定了父数组长度,父数组的每一个元素为 null
//(2)再确定子数组长度
二维数组名[下标] = new 子数组元素数据类型[该子数组长度];
// 没有赋值的父数组元素的值依旧为 null
// (3) 再为子数组的某个元素赋值
二维数组名[下标一][下标二] = 值;/**
* Description:
*/
public class Test25DifferentElementCount {
public static void main(String[] args) {
int[][] arr = new int[5][];
for (int i = 0; i < arr.length; i++) {
arr[i] = new int[i + 1];
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
arr[i][j] = i + 1;
}
}
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}数组的长度和角标
/**
* Description:
*/
public class Test22TwoDimensionalArrayUse {
public static void main(String[] args){
// 存储 3 个小组的学员的成绩,分开存储,使用二维数组。
// 声明并静态初始化
int[][] scores = {
{85,96,85,75},
{99,96,74,72,75},
{52,42,56,75}
};
System.out.println(scores); // [[I@15db9742
System.out.println("一共有 " + scores.length +" 组成绩。");
// [[:代表二维数组,I 代表元素类型是 int
System.out.println(scores[0]); // [I@6d06d69c
// [:代表一维数组,I 代表元素类型是 int
System.out.println(scores[1]); // [I@7852e922
System.out.println(scores[2]); // [I@4e25154f
//System.out.println(scores[3]); // ArrayIndexOutOfBoundsException: 3
System.out.println("第 1 组有 " + scores[0].length +" 个学员。");
System.out.println("第 2 组有 " + scores[1].length +" 个学员。");
System.out.println("第 3 组有 " + scores[2].length +" 个学员。");
System.out.println("第 1 组的每一个学员成绩如下:");
// 第一行的元素
System.out.println(scores[0][0]);// 85
System.out.println(scores[0][1]);// 96
System.out.println(scores[0][2]);// 85
System.out.println(scores[0][3]);// 75
// System.out.println(scores[0][4]); // java.lang.ArrayIndexOutOfBoundsException: 4
}
}*** 打印一个 10 行杨辉三角 ***
提示
第一行有 1 个元素,第 n 行有 n 个元素
每一行的第一个元素和最后一个元素都是 1
从第三行开始, 对于非第一个元素和最后一个元素的元素,值为
yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
杨辉三角,是二项式系数在三角形中的一种几何排列。在欧洲,这个表叫做帕斯卡三角形。 帕斯卡(1623----1662)是在 1654 年发现这一规律的,比杨辉要迟 393 年,比贾宪迟 600 年。 杨辉三角是中国古代数学的杰出研究成果之一,它把二项式系数图形化,把组合数内在的一些代数性质直观地从图形中体现出来, 是一种离散型的数与形的结合。
每行端点与结尾的数为 1,每个数等于它上方两数之和。 public class Yanghuisanjiao {
public static void main(String[] args) {
int[][] parentArr = new int[10][];
// 定义的变量 row col 都表示刻度
for (int row = 0; row < parentArr.length; row++) {
int[] childArr = new int[row + 1];
for (int col = 0; col < childArr.length; col++) {
if (col == 0 || col == row) {
childArr[col] = 1;
} else {
childArr[col] = parentArr[row - 1][col - 1] + parentArr[row - 1][col];
}
System.out.print(childArr[col] + " ");
}
System.out.println();
parentArr[row] = childArr;
}
}
}
还可以这样写:
public class Yanghuisanjiao2 {
public static void main(String[] args) {
// 1. 动态初始化的方式创建二维数组
int[][] yangHui = new int[10][];
for (int i = 0; i < yangHui.length; i++) {
yangHui[i] = new int[i + 1];
// 2. 给数组元素赋值
// 2.1 给外层数组元素中的首元素和末元素赋值
yangHui[i][0] = yangHui[i][i] = 1;
// 2.2 给外层数组元素中的非首元素和非末元素赋值(难)
// if(i > 1) { // 从 i == 2 开始执行
for (int j = 1; j < yangHui[i].length - 1; j++) { // 非首元素和非末元素的角标范围
yangHui[i][j] = yangHui[i - 1][j - 1] + yangHui[i - 1][j];
}
//}
}
// 3. 遍历二维数组
for (int i = 0; i < yangHui.length; i++) {
for (int j = 0; j < yangHui[i].length; j++) {
System.out.print(yangHui[i][j] + "\t");
}
System.out.println();
}
}
}