3、 数组

81 阅读7分钟

数组

1、数组的使用

数组的概述

数组就是装着相同数据类型变量的容器。数组中的变量称之为元素。

要使用数组,需要数组的初始化才可以使用

数组是大小固定不变,内存连续的。

数组的声明

int[] arr1;//推荐使用
int arr2[];

数组的初始化

//动态初始化
int[] arr1 = new int[5];

//静态初始化
int[] arr2 = new int[]{1,2,3,4,5};
int[] arr3 = {1,2,3,4,5};

数组元素的访问

int[] arr1 = {1,2,3,4,5};

Sysem.out.println("arr1[0] = " + arr1[0]);
//更改数组索引为0的值为6
arr1[0] = 6;
Sysem.out.println("arr1[0] = " + arr1[0]);
2、数组中的内存分配

数组内存分配的核心原则

  1. 数组变量(引用)存储在栈内存中,仅保存数组对象的内存地址。
  2. 数组对象(实际数据)存储在堆内存中,是连续的内存块。
  3. 堆内存中的数组对象会被 Java 垃圾回收器自动回收(当没有引用指向它时)。

一维数组的内存分配

int[] arr = new int[3]; 为例,内存分配过程如下:

  1. 声明数组变量int[] arr 在栈内存中创建一个引用变量 arr,此时未指向任何对象(值为 null)。
  2. 创建数组对象new int[3] 在堆内存中分配一块能存储 3 个 int 类型元素的连续空间,并自动初始化默认值(int 类型默认值为 0)。
  3. 关联引用与对象:栈内存中的 arr 变量保存堆内存中数组对象的地址,形成引用关系。
栈内存               堆内存
+--------+          +---------+
|  arr   |--------->| [0, 0, 0]|  // 长度为3的int数组,默认值为0
+--------+          +---------+
3、数组中常见的两个问题
  • ArrayIndexOutOfBoundsException 数组索引越界
  • NullPointerException 空指针异常
int[] arr = {1,2,3,4,5};
// ArrayIndexOutOfBoundsException  数组索引越界
System.out.println(arr[5]);

//`NullPointerException`  空指针异常
arr = null;
System.out.println(arr[0]);

扩展

// 使用equals方法时,尽量使非null字符串 .equals();减少空指针异常出现;
String str = null;
if("y".equals(str)){
    System.out.println("ok");
}
4、数组的遍历

for循环遍历

int[] arr = {1,2,3,4,5};
for(int i = 0;i < arr.length;i++){
    System.out.println(arr[i]);
}

使用JDK5特性增强for循环,缺点:不能通过操作数组元素

int[] arr = {1,2,3,4,5};
for(int i : arr){
    System.out.println(i);
}

使用Arrays.toString()直接打印

int[] arr = {1,2,3,4,5};
System.out.println(Arrays.toString(arr));
5、数组的最值

通过循环来遍历数组,并依次比较各元素的大小

int[] arr = {3,2,5,7,1,0,6};
int max = arr[0];
for(int i = 1; i < arr.length;i++){
    if(max < arr[i]){
        max = arr[i];
    }
}
System.out.println("arr数组的最大值为" + max);
6、数组的反转

数组的反转可以通过重新定义一个数组来存储反转后的数组实现

int[] arr1 = {1,2,3,4,5};
int[] arr2 = new int[arr1.length];

for(int i = 0; i < arr1.length; i ++) {
    arr2[arr1.length-1-i] = arr[i]; 
}
arr1 = arr2;
System.out.println("反转后的数组为" + Arrays.toString(arr1));

上述方法需要定义一个新的数组,不太推荐,思考是否有其他方案

发现可以直接交换数组元素,设置两个变量来表示索引

int[] arr = {1,2,3,4,5};

for(int min = 0,max = arr.length; min < max; min++,max--) {
    int temp = arr[min];
    arr[min] = arr[max];
    arr[max] = temp;
    //思考此处交换数据是否还有其它写法
}
System.out.println(Arrays.toString);

再次对上述代码进行优化得到

int[] arr = {1,2,3,4,5};

for(int i = 0; i < arr.length/2; i++) {
    int temp = arr[i];
    arr[i] = arr[arr.length - 1 -i];
    arr[arr.length - 1 -i] = temp;
}
System.out.println(Arrays.toString);
7、数组的查找

普通的查找,通过遍历数组依次比较

int[] arr = {1,2,3,4,5,6};
// 需要找到的值
int number = 3;
for(int i = 0;i < arr.length; i++) {
    if(arr[i] == number) {
        System.out.println("该值的索引为" + i);
        break;
    }
}

二分查找

对有序数组进行查找

int[] arr = {1,2,3,4,5,6,7,8,9};

int number = 8;
int min = 0;
int max = arr.length - 1;
int mid = (min + max)/2;
boolean ok = false;
while(min < max){
    if(arr[mid] < number){
        min = mid;
        mid = (min + max)/2;
    }else if(arr[mid] > number){
        max = mid;
        mid = (min + max)/2;
    }else if(arr[mid] == number){
        ok = true;
        System.out.println("该值的索引为" + mid);
        break;
    }
}
if(ok){
    System.out.println("该值没找到");
}
8、数组的排序

排序的方法有很多种,这里只介绍一下冒泡排序,选择排序

//可以直接使用Arrays.sort()进行排序,该排序会直接改变数组;
int[] arr = {1,4,5,7,32,6,69,23};
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));

冒泡排序

int[] arr = {1,4,5,7,32,6,69,23};

for(int i = 1;i < arr.length;i++){
    for(int j = 0;j < arr.length - i;j++){
        if(arr[j] < arr[j + 1]){
            //交换数据
        	int temp = arr[j];
            arr[j] = arr[j + 1];
            arr[j+1] = temp;
        }
    }
}
System.out.println(Arrays.toString(arr));

选择排序

int[] arr = {1,4,5,7,32,6,69,23};

for(int i = 0; i < arr.length-1; i++) {
    for(int j = i + 1; j < arr.length;j++) {
        if(arr[i] > arr[j]){
            //交换数据
        	int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
}
System.out.println(Arrays.toString(arr));
9、数组的扩容

数组的扩容本质上是创建一个新的数组,并且把老数组的数据全部拷贝过去

方法一:创建一个新数组,把老数组的数据全部拷贝过去;

int[] arr1 = {1,2,3,4,5};
System.out.println(Arrays.toString(arr1));
int[] arr2 = new int[arr1.length * 2];
for(int i = 0; i < arr1.length; i++){
    arr2[i] = arr1[i];
}
arr1 = arr2;
System.out.println(Arrays.toString(arr1));

方法二:使用Arrays.copyOf

int[] arr = {1,2,3,4,5};
System.out.println(Arrays.toString(arr));

//Arrays.copyOf需要两个参数
//参数1:需要扩容的数组
//参数2:扩容后的长度
Arrays.copyOf(arr,arr.length*2);
System.out.println(Arrays.toString(arr));

方法三:使用System.arraycopy

int[] arr1 = {1,2,3,4,5};
System.out.println(Arrays.toString(arr1));
int[] arr2 = new int[arr1.length * 2];

// 参数一: 要拷贝的数组
// 参数二: 要拷贝数组的数据的起始索引
// 参数三: 要拷贝到的数组
// 参数四: 要拷贝到的数组的数据的起始索引
// 参数五: 要拷贝的数据长度
System.arraycopy(arr1,0,arr2,0,5);
System.out.println(Arrays.toString(arr1));
10、二维数组[了解]

Java 二维数组的特点

  1. 本质是数组的数组:二维数组中的每个元素都是一个一维数组,因此各行的长度可以不同(称为 “不规则数组” 或 “锯齿数组”)。
  2. 声明与初始化分离:可以先声明数组,再初始化;也可以在声明时直接初始化。
  3. 内存存储:二维数组本身和内部的一维数组在内存中是分开存储的,并非必须是连续的内存块。
  4. 访问方式:通过两个索引(行索引和列索引)访问元素,索引从 0 开始。

声明与初始化方式

1. 声明时直接初始化

// 方式1:完整初始化(规则数组,行列固定)
int[][] matrix1 = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// 方式2:不规则数组(各行长度不同)
int[][] matrix2 = {
    {1, 2},
    {3},
    {4, 5, 6}
};

2.声明二维数组(不指定大小)

// 声明二维数组(不指定大小)
int[][] matrix3;

// 初始化:先指定行数,再分别初始化每行
matrix3 = new int[3][];  // 3行,列数暂不确定
matrix3[0] = new int[2]; // 第0行有2列
matrix3[1] = new int[3]; // 第1行有3列
matrix3[2] = new int[1]; // 第2行有1列

// 给元素赋值
matrix3[0][0] = 10;
matrix3[1][2] = 20;

3.声明时指定行列数(规则数组)

// 声明一个3行4列的规则数组(所有行长度相同)
int[][] matrix4 = new int[3][4];

// 赋值(默认初始值为0)
matrix4[0][0] = 1;
matrix4[2][3] = 5;

访问与遍历二维数组

1. 访问元素

通过 数组名[行索引][列索引] 访问具体元素:

int value = matrix1[1][2]; // 获取第1行第2列的元素(值为6)

2.遍历数组

常用 for 循环或增强 for 循环遍历:

// 方式1:普通for循环(适合需要索引的场景)
for (int i = 0; i < matrix1.length; i++) { // matrix1.length 是行数
    for (int j = 0; j < matrix1[i].length; j++) { // matrix1[i].length 是第i行的列数
        System.out.print(matrix1[i][j] + " ");
    }
    System.out.println(); // 换行
}

// 方式2:增强for循环(适合仅遍历元素的场景)
for (int[] row : matrix1) { // 遍历每行(row是一维数组)
    for (int num : row) {   // 遍历行中的每个元素
        System.out.print(num + " ");
    }
    System.out.println();
}

注意事项

  1. 数组长度二维数组名.length 表示行数;二维数组名[行索引].length 表示该行的列数。
  2. 默认值:未手动赋值的元素会有默认值(数值型为 0,布尔型为 false,引用类型为 null)。
  3. 空指针风险:如果某行未初始化(如 matrix3[0] = null),访问该行元素会抛出 NullPointerException