- 数组可以存放多个同一类型的数据。数组也是一种数据类型,是引用类型。 即:数(数据)组(一组)就是一组数据。
- 基本语法:
- 说明:int[] double = new double[5] 与 int double[] = new double[5] 这两者是等价的,习惯使用前者。
- 数组的引用=数组的使用=数组的访问=获取数组元素;这几种说法不同,但本质相同。
- 基本语法:
- 动态初始化的第二种方式是先声明一个数组的存在,但是先不给它分配内存空间,等到需要的时候再给它分配内存空间。
- 基本语法:
- 静态初始化数组,是在声明数组的同时便给数组的每个元素进行赋值;而动态初始化数组,数组中的每个元素是默认的初始值,需要我们另外遍历数组进行赋值。
- 数组是多个相同类型数据的组合,实现对这些数据的统一管理 ;
- 数组中的元素可以是任何相同的数据类型,包括基本类型和引用类型,但是不能混用;也可以存在向上转型的非相同的数据类型,如 double[] 中可以有元素int,但是会自动向上转型为double型。
- 数组创建后,如果没有赋值,有默认值:int 0,short 0,byte 0,long 0,float 0.0,double 0.0,char u0000,boolean false,String null ;
- 使用数组的步骤: 1. 声明数组并开辟空间; 2. 给数组各个元素赋值; 3. 使用数组;
- 数组的下标是从 0 开始的;
- 数组下标必须在指定范围内使用,否则报错:下标越界异常,比如 int [] arr = new int[5],则有效下标为 0-4;输出arr[5]会报错;
- 数组属引用类型,数组型数据是对象(object)。
请求出一个数组 int[]的最大值 {4, -1, 9, 10, 23},并得到其对应的下标
思路分析:
- 定义一个 int 数组 int[] arr = {4,-1,9, 10,23};
- 假定 max = arr[0] 是最大值 , maxIndex=0;
- 从下标 1 开始遍历 arr, 如果 max < 当前元素,说明 max 不是真正的最大值, 我们就 max=当前元素; maxIndex=当前元素下标;
- 当我们遍历这个数组 arr 后 , max 就是真正的最大值,maxIndex 最大值对应的下标。
- 代码实现:
public class ArrayExercise02 {
//编写一个 main 方法
public static void main(String[] args) {
int[] arr = {4,-1,9,10,23};
int max = arr[0];
//假定第一个元素就是最大值
int maxIndex = 0;
for(int i = 1; i < arr.length; i++) {
//从下标 1 开始遍历 arr
if(max < arr[i]) {
//如果 max < 当前元素
max = arr[i]; //把 max 设置成 当前元素
maxIndex = i;
}
}
//当我们遍历这个数组 arr 后 , max 就是真正的最大值,maxIndex 最大值下标
System.out.println("max=" + max + " maxIndex=" + maxIndex);
}
}
4. 数组赋值机制(重难点!)
- 基本类型的赋值方式是值传递/值拷贝,这两种说法本质上是相同的;
- 引用类型的赋值方式是引用传递/地址拷贝,这两种说法本质上是相同的;
- 举例说明(内存图):
- 代码解释:
// 基本类型的赋值
int n1 = 10;// 给基本类型变量赋值
int n2 = n1;// 将n1的值赋值给n2,实质上等同于int n2 = 10
n2 = 80;// 将80赋值给n2,不会影响n1 的值
// 在基本类型的赋值中,赋予给变量的是一个确定的值,每个变量对应着不同的栈内存空间,彼此之间不会相互影响。
// 引用类型的赋值
int[] arr1 = {1, 2, 3};// 在栈内存中声明了一个数组类型变量arr1,在堆内存中为该变量开辟了一个长度为3 的内存空间
int[] arr2 = arr1;// 将arr1的内存地址赋值给arr2,此时arr1和arr2共用一个堆内存空间
arr2[0] = 10;// 将100赋值给arr2中的第一个元素,相当于将10赋值给地址中的第一个元素,同时相当于改变了arr1中的第一个元素的值
// 在引用类型的赋值中,赋予给变量的是内存地址,此时多个变量对应着同一个堆内存空间,彼此之间相互影响。
5. 数组拷贝
- 那怎么创建一个和原数组相同,且内存空间相互独立,不会相互影响的新数组?答:需要新声明一个数组,然后采用for 循环遍历原数组,将原数组中的元素赋值给新数组。
- 代码如下:
public class ArrayCopy {
public static void main(String[] args) {
//将 int[] arr1 = {10,20,30}; 拷贝到 arr2 数组,
//要求数据空间是独立的. int[] arr1 = {10,20,30};
//创建一个新的数组 arr2,开辟新的数据空间 //大小 arr1.length;
int[] arr2 = new int[arr1.length];
//遍历 arr1 ,把每个元素拷贝到 arr2 对应的元素位置
for(int i = 0; i < arr1.length; i++) {
arr2[i] = arr1[i];
}
//修改 arr2中的元素, 不会对 arr1 有影响.
arr2[0] = 100;
//输出 arr1
System.out.println("====arr1 的元素====");
for(int i = 0; i < arr1.length; i++) {
System.out.println(arr1[i]);// 10,20, 30
}
System.out.println("====arr2 的元素====");
for(int i = 0; i < arr2.length; i++) {
System.out.println(arr2[i]);//
}
}
}
6. 数组翻转
- 将数组中的元素对称翻转;
- 代码如下:
// 方法一:观察规律
public class ArrayReverse {
//编写一个 main 方法
public static void main(String[] args) {
//定义数组
int[] arr = {11, 22, 33, 44, 55, 66};
int temp = 0;
int len = arr.length;
//计算数组的长度
for( int i = 0; i < len / 2; i++) {
temp = arr[len - 1 - i];//保存
arr[len - 1 - i] = arr[i]; arr[i] = temp;
}
System.out.println("===翻转后数组===");
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "t");//66,55,44,33,22,11
}
}
}
// 方法二:采用逆序赋值
public class ArrayReverse02 {
//编写一个 main 方法
public static void main(String[] args) {
//定义数组
int[] arr = {11, 22, 33, 44, 55, 66};
//使用逆序赋值方式
//老韩思路
//1. 先创建一个新的数组 arr2 ,大小 arr.length
//2. 逆序遍历 arr ,将 每个元素拷贝到 arr2 的元素中(顺序拷贝)
//3. 建议增加一个循环变量 j -> 0 -> 5
int[] arr2 = new int[arr.length];
//逆序遍历 arr
for(int i = arr.length - 1, j = 0; i >= 0; i--, j++) {
arr2[j] = arr[i];
}
//4. 当 for 循环结束,arr2 就是一个逆序的数组 {66, 55, 44,33, 22, 11}
//5. 让 arr 指向 arr2 数据空间, 此时 arr 原来的数据空间就没有变量引用会被当做垃圾,销毁 arr = arr2;
System.out.println("====arr 的元素情况=====");
//6. 输出 arr 看看
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "t");
}
}
}
- 思考:在方法二中,使用了引用类型变量的赋值方式引用传递,让原数组和新数组共用了一个堆内存空间,此时原数组的地址发生了改变,原地址因为没有数组的使用而被虚拟机回收清理。
- 要求:实现动态的给数组添加元素,实现对数组扩容。
- 原始数组使用静态分配 int[] arr = {1,2,3} ;
- 添加的元素 4,直接放在数组的最后 arr = {1,2,3,4} ;
- 用户可以通过如下方法来决定是否继续添加,添加成功,是否继续?y/n
- 思路分析:
1. 定义初始数组 int[] arr = {1,2,3} //下标 0-2;
2. 定义一个新的数组 int[] arrNew = new int[arr.length+1];
3. 遍历 arr 数组,依次将 arr 的元素拷贝到 arrNew 数组 ;
4. 将 4 赋给 arrNew[arrNew.length - 1] = 4;把 4 赋给 arrNew 最后一个元素 ;
5. 让 arr 指向 arrNew ; arr = arrNew;原来分配给 arr的堆内存空间会被销毁 ;
6. 创建一个 Scanner 可以接受用户输入 ;
7. 因为用户什么时候退出,不确定,使用 do-while + break 来控制 。
- 代码如下:
public class ArrayAdd02 {
public static void main(String[] args) {
//要求:实现动态的给数组添加元素效果,实现对数组扩容。
Scanner myScanner = new Scanner(System.in);
//初始化数组
int[] arr = {1,2,3};
do {
int[] arrNew = new int[arr.length + 1];
//遍历 arr 数组,依次将 arr 的元素拷贝到 arrNew 数组
for(int i = 0; i < arr.length; i++) {
arrNew[i] = arr[i];
}
System.out.println("请输入你要添加的元素");
int addNum = myScanner.nextInt();
//把 addNum 赋给 arrNew 最后一个元素
arrNew[arrNew.length - 1] = addNum;
//让 arr 指向 arrNew,
arr = arrNew;
//输出 arr 看看效果
System.out.println("====arr 扩容后元素情况====");
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "t");
}
//问用户是否继续
System.out.println("是否继续添加 y/n");
char key = myScanner.next().charAt(0);
if( key == 'n') {
//如果输入 n ,就结束
break;
}
} while(true);
System.out.println("你退出了添加...");
}
}
8. 数组缩减
- 数组缩减的思路是和数组扩容几乎完全一样的。
- 代码如下:
public class ArrayAdd02 {
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
//初始化数组
int[] array1 = {1, 2, 3, 4, 5};
do {
int[] array2 = new int[array1.length-1];
for (int i = 0; i < array2.length; i++) {
if (i < array1.length) {
array2[i] = array1[i];
}
}
array1 = array2;
for (int i = 0; i < array1.length; i++) {
System.out.print(array1[i]);
}
if (array1.length == 1) {
System.out.println("数组中只剩下最后一个元素,不能再缩减!");
break;
}
System.out.println("你是否还要缩减y/n");
char c = sc.next().charAt(0);
if (c == 'n') {
break;
}
} while (true);
}
}
二、排序
- 排序的分类:
- 内部排序:指将需要处理的所有数据都加载到内部存储器中进行排序。包括(交换式排序法、选择 式排序法和插入式排序法);
- 外部排序:数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。包括(合并排序法和直接合并排序法)。
- 此处只介绍一种简单的排序:冒泡排序;其他的排序算法,博主在以后会专门写一遍博客来分享。
- 冒泡排序(Bubble Sorting)的基本思想 :通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素 的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
举例解释:将一个无序的数组:{24, 69, 80, 57, 13} 使用冒泡排序法将其排成一个从小到大的有序数组。
- 思路分析:图示
- 代码实现:
public class BubbleSort {
public static void main(String[] args) {
// 冒泡排序
// 补充:优化策略:如果在某一次比较中,没有元素交换,则表示已经排序好了,可以break;(不用理)
int[] arr = {24, 69, 80, 57, 13};
// 有n个元素,只需要排序n-1轮,每一轮会将一个元素放到正确的位置
// 则剩下的最后一个元素就不需要排序了
for (int i = 0; i < arr.length - 1; i++) {
int tmp = 0;// 用于辅助交换的变量
// 有n个元素,每一轮只需要比较n-1次,n是在不断变化的
for (int k = 0; k < arr.length - i - 1; k++) {
// 如果前面的元素>后面的元素,就交换
if (arr[k] > arr[k + 1]) {
tmp = arr[k];
arr[k] = arr[k + 1];
arr[k + 1] = tmp;
}
}
}
// 遍历输出排序后的数组
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
}
三、查找
- 在 java 中,我们常用的查找有两种:
- 顺序查找
- 二分查找【二分法,此处不涉及】
举例:有一个数组 {1, 3, 5, 7, 9},从键盘中任意输入的一个数,要求: 如果找到了,就提示找到,并给出下标值;否则提示未找到。
- 代码实现:
public class SeqSearch {
public static void main(String[] args) {
// 顺序查找
Scanner sc = new Scanner(System.in);
int[] arr = {1, 3, 5, 7, 9};
System.out.println("请输入一个数:");
int input = sc.nextInt();
int index = -1;// 一个编程技巧,用来判断是否进入一个代码块
for (int i = 0; i < arr.length; i++) {
if (input == arr[i]) {
System.out.println("你找到了" + arr[i]);
index = i;// 一个标识符,用来判断是否成功进入该代码块,若进入,则改变该标识符的值,否则该标识符的值仍为初始值
break;// 找到该元素,直接退出循环
}
}
// index 未改变,说明没有找到需要的元素
if (index == -1) {
System.out.println("该数组中没有你要找的数!");
}
}
}
四、二维数组
1. 快速入门
- 什么是二维数组:
- 从定义形式上看 int [][];
- 可以这样理解,一个一维数组的每个元素都是一维数组, 这样就构成了二维数组。
- 举例:
int[][] arr = { {0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0},
{0,2, 0, 3, 0, 0}, {0, 0, 0, 0, 0, 0} };
- 关于二维数组的关键概念:
- 二维数组的元素个数 = 二维数组中一维数组的个数 = arr.length;
- 二维数组的每个元素是一维数组, 所以如果需要得到每个一维数组中的元素,还需要再次遍历;
- arr[i][j] =》 二维数组的第 i +1 个一维数组的第 j+1 个元素; eg: arr[0][0] = arr 中的第1个一维数组中的第1个元素。
例子:输出二维数组中的每一个元素
public class TwoDimensionalArray01 {
public static void main(String[] args) {
int[][] arr = { {0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0},
{0,2, 0, 3, 0, 0}, {0, 0, 0, 0, 0, 0} };
for(int i = 0; i < arr.length; i++) {
//遍历二维数组的每个元素(数组)
//1. arr[i] 表示 二维数组的第 i+1 个元素 比如 arr[0]:二维数组的第一个元素
//2. arr[i].length 得到 对应的 每个一维数组的长度
for(int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
//输出了一维数组
}
System.out.println();//换行
}
}
}
2. 数组初始化
2.1 静态初始化
- 基本语法:
- 类型[][] 数组名 = {{值 1,值 2…},{值 1,值 2…},{值 1,值 2…}};
- 举例:int[][] arr = {{1,1,1}, {8,8,9}, {100}};
- 注意:二维数组的每个元素必须是一维数组,不允许是其他数据类型;每个一维数组中的元素个数可以相等/不相等。上例中,第一个一维数组有 3 个元素 , 第二个一维数组有 3 个元素, 第三个一维数组只有 1 个元素。
- 基本语法:
- 类型[][] 数组名 = new 类型[大小][大小];
- 举例: int arr[][] = new int[2][3];
- 这种初始化方式在声明二维数组的同时开辟了其所需要的堆内存空间;
- 基本语法:
- 先声明:类型[][] 数组名;
- 再定义(开辟空间):数组名 = new 类型[大小][大小];
- 然后给一维数组中的元素赋值,若不赋值,则为默认值。
- 基本语法:
- 先声明:类型[][] 数组名;
- 再定义(开辟空间):数组名 = new 类型[大小][]; 注意:此时一维数组未开辟堆内存空间;
- 遍历二维数组,给一维数组开辟堆内存空间,此时开辟的空间可以不相等;也就是列数不确定。
- 最后给一维数组中的元素赋值。
- 代码说明:
int[][] arr = new int[3][];// 此时一维数组还没有分配内存空间;arr[i]的地址为null;
for (int i = 0; i < arr.length; i++) {
arr[i] = new int[i + 1];// 给每个一维数组开辟空间,若没有这一步,一维数组的内存空间就是null;
}
3. 二维数组的内存存在形式
- 以动态初始化的第一种方式为例,int arr[][] = new int[2][3]; 其内存示意图如下:
- 说明:
- 首先在栈内存中声明了一个二维数组类型的变量 arr,在堆内存中为其开辟了一个大小为 2 的地址空间,里面分别存储着一维数组 arr[0]、arr[1] 的地址;
- 同时在堆内存中另外开辟了两个新的大小为 3 的内存空间,空间的地址就是 arr[0]、arr[1] 的内存地址,内存里面存储着一维数组中的元素。
- 注意:如果是以动态初始化(列数不确定)的方式创建二维数组,例如:
int[][] arr = new int[3][]; 则此时所有的一维数组都没有开辟内存空间,它们的地址都为null;
使用二维数组打印一个 10 行杨辉三角,如下图所示:
图示:
规律:
1.第一行有 1 个元素, 第 n 行有 n 个元素 ;
2. 每一行的第一个元素和最后一个元素都是 1 ;
3. 从第三行开始, 对于非第一个元素和最后一个元素的元素的值 arr[i][j] ,arr[i][j] = arr[i-1][j] + arr[i-1][j-1]。
- 代码实现:
public class TwoDimentionalArray01 {
public static void main(String[] args) {
int[][] a = new int[10][];
for (int i = 0; i < a.length; i++) {
// 给每个一维数组开辟堆内存空间,第n行有n个元素
a[i] = new int[i + 1];
// 遍历每一个一维数组,赋值
for (int j = 0; j < a[i].length; j++) {
// 每一行的第一个元素和最后一个元素都是1
if (j == 0 || j == a[i].length - 1) {
a[i][j] = 1;
} else {
// 每一行非第一个元素和最后一个元素的值 = 上一行的同一列 + 上一行的上一列
a[i][j] = a[i - 1][j] + a[i - 1][j - 1];
}
}
}
// 输出杨辉三角
for (int i = 0; i < a.length; i++) {
for (int k = 0; k < a[i].length; k++) {
System.out.print(a[i][k] + "t");
}
System.out.println();
}
}
}
5. 二维数组使用细节和注意事项
(1)一维数组的声明方式有: int[] x 或者 int x[] ;
(2)二维数组的声明方式有: int[][] y 或者 int[] y[] 或者 int y[][] ;
(3)二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同。 比如: map[][] 是 一个二维数组, int[][] map = {{1,2},{3,4,5}} ;其中,map[0] 是一个含有两个元素的一维数组 ,map[1] 是一个含有三个元素的一维数组,因此, map[][] 也称为 “列数不等的二维数组”。
- 本文是小白博主在学习B站韩顺平老师的Java网课时整理的学习笔记,在这里感谢韩顺平老师的网课,如有有兴趣的小伙伴也可以去看看。
- 第二部分的学习总结就结束啦,明天开始第三部分,如果本文有什么错漏的地方,欢迎大家批评指正!一起加油!!



