Skip to content

Java 中的数组

数组的概念

数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。

数组中的概念:

  • 数组名
  • 下标(或索引,或偏移量)
  • 元素
  • 数组的长度

数组的特点

  • 数组本身是引用数据类型,而数组中的元素可以是任何数据类型,包括基本数据类型和引用数据类型。
  • 创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组的长度和数组中元素的类型。
  • 数组中的元素在内存中是依次紧密排列的,有序的。
  • 数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改
  • 我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。
  • 数组名中引用的是这块连续空间的首地址。

数组的分类

1、按照元素类型分:

  • 基本数据类型元素的数组:每个元素位置存储基本数据类型的值
  • 引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)

2、按照维度分:

  • 一维数组:存储一组数据
  • 二维数组:存储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样

一维数组的使用

一维数组的声明

格式:

// 推荐
元素的数据类型[] 一维数组的名称;

// 不推荐
元素的数据类型  一维数组名称[];

举例:

int[] arr;
int arr1[];
double[] arr2;
String[] arr3; // 引用类型变量数组

数组的声明,需要明确:

-(1)数组的维度:在 Java 中数组的符号是 [],[] 表示一维,[][] 表示二维。 -(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的 Java 的数据类型。例如:int、String、Student 等。 -(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。

举例:

java
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 整数的数组容器。

java
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}; // 错误

举例:

java
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:正确写法

java
int[] arr = new int[5];

int[] arr;
arr = new int[5];

举例 2:错误写法 花括号里 {} 指定了元素列表,就不需要在 [] 中指定元素个数了。

java
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:

java
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:

java
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("]");
    }
}

数组元素的默认值

数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。例如:

java
public class ArrayTest6 {
	public static void main(String argv[]){
		int a[]= new int[5]; 
		System.out.println(a[3]); // a[3] 的默认值为 0
	}
}

对于基本数据类型而言,默认初始化值各有不同。

对于引用数据类型而言,默认初始化值为 null,注意与 0 不同!

数组元素的类型元素的默认初始值
byte0
short0
int0
long0L
float0.0f
double0.0
char0 或写为 '\u0000'(显示为空)
booleanfalse
引用类型null
java
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 中的寄存器,它包含每一个线程下一条要执行的指令的地址。

一维数组在内存中的存储

java
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 行业人士,喜欢安静。所以要求来租者最好是同行或者刚毕业的年轻人,爱干净、安静。电话号码如下:

java
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"}

java
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 数组,存放学生成绩。

java
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 年全年各个月份的销售额进行登记。按月份存储,可以使用一维数组。如下:

java
int[] monthData = new int[]{23,43,22,34,55,65,44,67,45,78,67,66};

如果改写为按季度为单位存储怎么办呢?

java
int[][] quarterData = new int[][]{{23,43,22},{34,55,65},{44,67,45},{78,67,66}};

应用举例 2:

高一年级三个班级均由多个学生姓名构成一个个数组。如下:

java
String[] class1 = new String[]{"段誉","令狐冲","任我行"};

String[] class2 = new String[]{"张三丰","周芷若"};

String[] class3 = new String[]{"赵敏","张无忌","韦小宝","杨过"};

那从整个年级看,我们可以声明一个二维数组。如下:

java
String[][] grade = new String[][]{{"段誉","令狐冲","任我行"},{"张三丰","周芷若"},{"赵敏","张无忌","韦小宝","杨过"}};

声明

二维数组声明的语法格式:

// 推荐
子数组元素的数据类型[][] 二维数组的名称;

// 不推荐
子数组元素的数据类型  二维数组名[][];
// 不推荐
子数组元素的数据类型[]  二维数组名[];

例如:

java
public class Test20TwoDimensionalArrayDefine {
    public static void main(String[] args) {
        //存储多组成绩
        int[][] grades;

        //存储多组姓名
        String[][] names;
    }
}
java
int[] x, y[];
// x 是一维数组,y 是二维数组

静态初始化

格式:

java
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) 再为子数组的某个元素赋值
二维数组名[下标一][下标二] = 值;
java
/**
 * 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();
        }

    }
}

数组的长度和角标

java
/**
 * 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. 第一行有 1 个元素,第 n 行有 n 个元素

  2. 每一行的第一个元素和最后一个元素都是 1

  3. 从第三行开始, 对于非第一个元素和最后一个元素的元素,值为 yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];

杨辉三角,是二项式系数在三角形中的一种几何排列。在欧洲,这个表叫做帕斯卡三角形。 帕斯卡(1623----1662)是在 1654 年发现这一规律的,比杨辉要迟 393 年,比贾宪迟 600 年。 杨辉三角是中国古代数学的杰出研究成果之一,它把二项式系数图形化,把组合数内在的一些代数性质直观地从图形中体现出来, 是一种离散型的数与形的结合。

每行端点与结尾的数为 1,每个数等于它上方两数之和。
java
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;
        }

    }
}

还可以这样写:

java
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();
        }

    }
}

Released under the MIT License.