Java 的数组详解
数组的定义
-
数组是相同类型数据的有序集合
-
数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成
-
其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标(编号、标记)来访问它,下标是从 0 开始的,如果是存 10 个数组,那么下标是从 0 ~ 9 的
-
代码举例
public class ArrayDemo01 { //变量的类型 变量的名字 = 变量的值 public static void main(String[] args) { int[] nums = {1,2,3,4,5,6,7,8,9,10}; System.out.println(nums[0]); //1 打印下标元素为0的内容 } }
数组的声明创建
-
首先必须声明数组变量,才能在程序中使用数组
-
语法
dataType[] arrayRefVar; //首选的方法 dataTypr arrayRefVar[]; //效果相同,但不建议
-
代码举例
//数组类型[] 数组名字; int[] nums; int nums[];
-
-
Java 语言使用 new 操作符来创建数组
-
语法
dataType[] arrayRefVar = new dataType[srraySise];
-
代码举例
//数组类型[] 数组名字 = new 数组类型[数组容量]; int[] nums = new int[10]
-
-
数组的元素是通过索引访问的,数组索引从 0 开始
-
获取数组长度
//数组名字.length arrays.length //nums.length
-
代码总结举例
public class ArrayDemo01 { //变量的类型 变量的名字 = 变量的值 public static void main(String[] args) { //int[] nums = {1,2,3,4,5,6,7,8,9,10}; //一次性给数组元素赋值 //System.out.println(nums[0]); //1 //2种定义数组方法 int[] nums; //首选定义方法 int nums2[]; //第二种定义方法 C语言语法,为了让C语言程序员而兼容。 //int[] nums = new int[10]; //一步到位定义好存放数组大小 nums = new int[10]; //这里面可以存放 10 个 int 类型的数字 //依次给数组元素赋值 nums[0] = 1; nums[1] = 2; nums[2] = 3; nums[3] = 4; nums[4] = 5; nums[5] = 6; nums[6] = 7; nums[7] = 8; nums[8] = 9; nums[9] = 10; //计算所有元素的和 int sum = 0; //获取数组长度:arrays.length for (int i = 0; i < nums.length; i++) { sum = sum + nums[i]; } System.out.println("总和为:" + sum); //55 } }
数组的内存
-
数组内存分析
-
画图分析数组内存
数组的三种初始化
-
静态初始化:创建的同时赋值
-
代码语法举例
int[] a = {1, 2, 3}; //正常的静态初始化 Man[] mans = {new Man(1, 1), new Man(2, 2)}; //引入的静态初始化
-
-
动态初始化:包含默认初始化,未赋值的元素默认为 0
-
代码语法举例
int[] a = new int[3]; a[0] = 1; a[1] = 2; System.out.println(a[0]); //1 System.out.println(a[1]); //2 System.out.println(a[3]); //0 未赋值则为 0 默认初始化
-
-
数组的默认初始化:数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化(0)
-
代码语法举例
int[] a = new int[1]; System.out.println(a[1]); //0 未赋值则为 0
-
数组的四个基本特点
- 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的
- 其元素必须是相同类型,不允许出现混合类型
- 数组中的元素可以是任何数据类型,包括基本类型和引用类型
- 数组变量属于引用类型,数组也可以看成是对象,数组指定每个元素相当于该对象的成员变量。数组本身就是对象,Java 中对象是在堆中的,因此数组无论保护原始类型还是其他对象类型,数组对象本身是在堆中的
数组的边界
-
下标的合法区间:[0, length-1],如果越界就会报错 ArrayIndexOutOfBoundsException (数组下标越界异常)
-
代码举例
public class ArrayDemo02 { public static void main(String[] args) { //静态初始化:创建的同时赋值 int[] a = {1, 2, 3, 4, 5, 6, 7, 8}; for (int i = 0; i < a.length; i++) { System.out.println(a[i]); //正常遍历出 a 这个数组内所有值 } /* for (int i = 0; i <= a.length; i++) { //将 i < a.length 改为 i <= a.length System.out.println(a[i]); //先报错随后正常遍历出a这个数组内所有值 //下标为 数组长度 -1,例如 a[0] = 1 而不是 a[1] = 1。当 for 循环走到 i <= a.length 时,下标为 8 (a[8]),a(8)未赋值且超过数组最大长度因此会越界报错 } */ } }
-
-
结论
- 数组是相同数据类型 (数据类型可以为任意类型) 的有序集合
- 数组也是对象。数组元素相当于对象的成员变量
- 数组长度是确定不可改变的。越界则报错 ArrayIndexOutOfBoundsException (数组下标越界异常)
数组的使用
-
普通的 For 循环
public class ArrayDemo03 { public static void main(String[] args) { int[] arrays = {1, 2, 3, 4, 5}; //打印全部的数组元素 for (int i = 0; i < arrays.length; i++) { System.out.println(arrays[i]); } //计算所有元素的和 int sum = 0; for (int i = 0; i < arrays.length; i++) { sum += arrays[i]; //sum = sum + arrays[i] } System.out.println("sum=" + sum); //查找最大元素 int max = arrays[0]; for (int i = 1; i < arrays.length; i++) { //arrays[0] 已经是 max 的默认值,因此我们改成 i = 1 ,从 1 开始 if (arrays[i] > max){ max = arrays[i]; } } System.out.println("max=" + max); } }
-
For-Each 循环
public class ArrayDemo04 { public static void main(String[] args) { int[] arrays = {1, 2, 3, 4, 5}; //JDK1.5特性 只遍历全部值,没有下标 for (int array : arrays) { //增强型 for 循环,关键字 arrays.for 自动生成 System.out.println(array); } } }
-
数组作方法入参
public class ArrayDemo04 { public static void main(String[] args) { int[] arrays = {1, 2, 3, 4, 5}; //调用打印数组元素方法 printArray(arrays); //更好的遍历数组,在需要时可以取到下标 } //打印数组元素 public static void printArray(int[] arrays){ //更好的遍历数组,在需要时可以取到下标 for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i] + " "); } } }
-
数组作返回值
public class ArrayDemo04 { public static void main(String[] args) { int[] arrays = {1, 2, 3, 4, 5}; int[] result = reverse(arrays); //调用反转数组方法 printArray(result); //更好的遍历数组,在需要时可以取到下标 } //打印数组元素 public static void printArray(int[] arrays){ //更好的遍历数组,在需要时可以取到下标 for (int i = 0; i < arrays.length; i++) { System.out.print(arrays[i] + " "); } } //反转数组 public static int[] reverse(int[] arrays){ int[] result = new int[arrays.length]; //保证传入的数组不会超出最大长度 //反转的操作 for (int i = 0, j = result.length-1; i < arrays.length; i++, j--) { //result.length-1 是因为下标从 0 开始而 length 长度是从 1 开始,一次使用两个组合 for 循环 result[j] = arrays[i]; } return result; //返回值 } }
多维数组
-
多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一堆数组,其每一个元素都是一个一堆数组
-
二维数组
-
语法
int a[][] = new int[2][5]; int[][] a = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}};
-
代码举例
public class ArrayDemo05 { public static void main(String[] args) { int[][] array = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}}; //int array[][] = new int[2][5]; //相当于一个数组内嵌套了 2 层的数组 /* //解析如下: a = { {1, 2}, //2 层嵌套的数组 {3, 4}, {5, 6}, {7, 8}, {9, 10}, } */ //打印指定元素的值 System.out.println(array[0][0]); //1 System.out.println(array[0][1]); //2 System.out.println(array[1][1]); //4 System.out.println(array.length); //5 5列 System.out.println(array[0].length); //2 2行 } }
-
解析:以上二维数组 a 可以看成一个两行五列的数组
-
-
三维数组
-
语法
int a[][][] = new int[][][]; int[][][] a = {{{1, 2}, {3, 4}}};
-
代码举例
public class ArrayDemo05 { public static void main(String[] args) { int[][][] array = {{{1, 2}, {3, 4}},{{5, 6}, {7, 8}}, {{9, 10}, {11, 12}}}; //int array[][][] = new int[2][2][3]; //相当于一个数组内嵌套了 3 层的数组 /* //解析如下: a = { { {1, 2}, {3, 4}, }, { {5, 6}, {7, 8}, }, { {9, 10}, {11, 12}, }, //3 层嵌套的数组 } */ //打印指定元素的值 System.out.println(array[0][0][0]); //1 System.out.println(array[0][0][1]); //2 System.out.println(array[1][1][1]); //8 System.out.println(array.length); //3 3组 System.out.println(array[0].length); //2 2列 System.out.println(array[0][1].length); //2 2行 } }
-
解析:以上三维数组 a 可以看成一个两行两列三组的数组
-
-
...
-
-
遍历多维数组
-
遍历二维数组代码举例
public class ArrayDemo05 { public static void main(String[] args) { int[][] array = {{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}}; //遍历 for (int i = 0; i < array.length; i++) { for (int j = 0; j < array[i].length; j++) { System.out.println(array[i][j]); } } } }
-
遍历三维数组代码举例
public class ArrayDemo06 { public static void main(String[] args) { int[][][] array = {{{1, 2}, {3, 4}},{{5, 6}, {7, 8}}, {{9, 10}, {11, 12}}}; //遍历 for (int i = 0; i < array.length; i++) { for (int j = 0; j < array[i].length; j++) { for (int k = 0; k < array[i][j].length; k++) { System.out.println(array[i][j][k]); } } } } }
-
...
-
数组的 Arrays 类
-
调用数组的工具类 java.util.Arrays
import java.util.Arrays; //调用工具类 public class ArraysDemo07 { public static void main(String[] args) { int[] a = {1,2,3,4,3554,65,6,231324}; Arrays.sort(a);//所有的 Arrays 都需要调用 import java.util.Arrays; 工具类 }
-
由于数组对象并没有什么方法可以供我们调用,但 API 中提供了一个工具类 Arrays 供我们使用,从而可以对数据对象进行一些基础操作
-
查看 Arrays 类内所有方法
-
方法①:打开 JDK 帮助文档(搜索 Arrays)查看
-
方法②:直接使用 IDEA 查看源代码
-
创建 main 方法后键入"Arrays"如下图,点击即可调用它的工具类
-
鼠标停放在调用的工具类 java.util.Arrays 的 Arrays 关键字几秒会弹出一个窗口,以下操作后点击 跳转到源 可以索引到方法源
-
进入 Arrays 源代码内后下方可以看到 Arrays 点击它即可自行查看 Arrays 内所有的源方法以及使用方法
-
-
-
-
Arrays 类中的方法都是 static 修饰的静态方法,在使用的时候可以直接使用类名进行调用,而“不用”使用对象来调用(注意:是“不用”而不是“不能”)
import java.util.Arrays; public class ArraysDemo07 { public static void main(String[] args) { int[] a = {1,2,3,4,3554,65,6,231324}; //直接调用 printArray(a); //[1, 2, 3, 4, 3554, 65, 6, 231324] } //创建方法 使用 static 修饰符 public static void printArray(int[] a){ System.out.print("["); for (int i = 0; i < a.length; i++) { String symbol = ""; if (i != a.length-1){ symbol = ", "; }else{ symbol = ""; } System.out.print(a[i] + symbol); } System.out.println("]"); } }
-
它具有以下常用功能:
-
打印数组元素:通过 toString 方法直接遍历
import java.util.Arrays; public class ArraysDemo07 { public static void main(String[] args) { int[] a = {1,2,3,4,3554,65,6,231324}; //打印数组元素:Arrays.tostring System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 3554, 65, 6, 231324] //了解原理后咱们也可以自己创建方法来实现 //可以但不建议,大家了解一下就好。不建议重复造轮子! printArray(a); //[1, 2, 3, 4, 3554, 65, 6, 231324] } //创建方法 public static void printArray(int[] a){ System.out.print("["); for (int i = 0; i < a.length; i++) { String symbol = ""; if (i != a.length-1){ symbol = ", "; }else{ symbol = ""; } System.out.print(a[i] + symbol); } System.out.println("]"); } }
-
给数组重新赋值:通过 fill 方法重新填充
import java.util.Arrays; public class ArraysDemo07 { public static void main(String[] args) { int[] a = {1,2,3,4,3554,65,6,231324}; //数组填充(重新赋值) //Arrays.fill(a, 0); //全部重新赋值为 0 //System.out.println(Arrays.toString(a)); //[0, 0, 0, 0, 0, 0, 0, 0] Arrays.fill(a, 2, 4, 0); //包括下标为 2 但不包括下标为 4 之间的元素重新赋值为 0 System.out.println(Arrays.toString(a)); //[1, 2, 0, 0, 3554, 65, 6, 231324] } }
-
对数组排序:通过 sort 方法,按升序排序
import java.util.Arrays; public class ArraysDemo07 { public static void main(String[] args) { int[] a = {1,2,3,4,3554,65,6,231324}; Arrays.sort(a);//数组进行排序:升序 System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 6, 65, 3554, 231324] } }
-
比较数组:通过 equals 方法比较数组中元素值是否相等
import java.util.Arrays; public class ArraysDemo07 { public static void main(String[] args) { int[] a = {1,2,3,4,3554,65,6,231324}; //给数组中的元素值比较是否相等 int[] b = {0}; if(Arrays.equals(a, b)) { //不相等 System.out.println("相等"); }else{ System.out.println("不相等"); } int[] c = {1,2,3,4,3554,65,6,231324}; if(Arrays.equals(a, c)) { //相等 System.out.println("相等"); }else{ System.out.println("不相等"); } } }
-
查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作
import java.util.Arrays; public class ArraysDemo07 { public static void main(String[] args) { int[] a = {1,2,3,4,3554,65,6,231324}; //对数组进行二分查找法操作 Arrays.sort(a); //使用二分查找法的数组必须是有序的 System.out.println(Arrays.binarySearch(a, 0)); //2 查找元素值 3,找到后返回下标。 /* 若未找到元素值则会出现以下情况: [1] 搜索值不是数组元素值,大小在所有数组范围内,下标从1开始计数,返回“ - 按顺序插入点的下标” [2] 搜索值是数组元素值,下标从0开始计数,返回搜索值的下标 [3] 搜索值不是数组元素值,且大于数组内所有元素值,返回 – (数组最大长度 + 1) [4] 搜索值不是数组元素值,且小于数组内所有元素值,返回 – 1 */ } }
-
...
-
数组的冒泡排序
-
冒泡排序无疑是最为出名的排序算法之一 (总共有八大排序)
-
冒泡排序的步骤 (两两比较)
-
比较数组中,两个相邻的元素,如果第一个数比第二个数大,我们就将它们交换位置
-
每一次比较,都会产生出一个最大或者最小的数字
-
下一轮则可以少一次排序
-
依次循环,直到结束
-
-
代码举例
import java.util.Arrays; public class ArrayDemo08 { public static void main(String[] args) { int[] a = {2,4,2,1,3,5,6,66,77,7,4,0}; int[] sort = sort(a); //调用完我们创建的排序方法后返回一个排序后的数组 System.out.println(Arrays.toString(sort)); } //冒泡排序方法 public static int[] sort(int[] array){ //临时变量 int temp = 0; //外层循环:判断我们这个要走多少次 for (int i = 0; i < array.length-1; i++) { //当循环走到最后的下标时无法与下个下标进行比较就会溢出,因此这里需要 array.length-1 预留一个位置进行比较排序 //内层循环:比较判断两个数,如果第一个数比第二个数大,则交换位置 for (int j = 0; j < array.length-1-i; j++) { //每次遍历比较都要排除掉i且给最后下标的元素值预留1个位置,因此需要 array.length-1-i if (array[j+1]<array[j]){ //判断比较数组的下个下标的数是否大于目前下标的数 temp = array[j]; //将本次对比的元素值存进临时变量 array[j] = array[j+1]; array[j+1] = temp; } } } return array; //排序完成后返回数组 } }
-
-
冒泡的代码还是相当简单的,两层循环,外层冒泡轮数,里层依次比较,江湖中人尽皆知
-
我们看到嵌套循环,应该立马就可以得出这个算法的时间复杂度为 O(n2)
-
优化代码举例
import java.util.Arrays; public class ArrayDemo08 { public static void main(String[] args) { int[] a = {2,4,2,1,3,5,6,66,77,7,4,0}; int[] sort = sort(a); //调用完我们创建的排序方法后返回一个排序后的数组 System.out.println(Arrays.toString(sort)); } //冒泡排序方法 public static int[] sort(int[] array){ //临时变量 int temp = 0; //外层循环:判断我们这个要走多少次 for (int i = 0; i < array.length-1; i++) { //当循环走到最后的下标时无法与下个下标进行比较就会溢出,因此这里需要 array.length-1 预留一个位置进行比较排序 boolean flag = false; //通过 flag 标识减少没有意义的比较 //内层循环:比较判断两个数,如果第一个数比第二个数大,则交换位置 for (int j = 0; j < array.length-1-i; j++) { //每次遍历比较都要排除掉i且给最后下标的元素值预留1个位置,因此需要 array.length-1-i if (array[j+1]<array[j]){ //判断比较数组的下个下标的数是否大于目前下标的数 temp = array[j]; //将本次对比的元素值存进临时变量 array[j] = array[j+1]; array[j+1] = temp; flag = true; } } if (flag == false){ //只要未执行以上循环则会终止所有循环继续走下面的代码。节省时间成本 break; } } return array; //排序完成后返回数组 } }
稀疏数组
-
稀疏数组的介绍
-
当一个数组中大部分元素为 0,或者为同一数值的数组时,可以使用稀疏数组来保存该数组
-
稀疏数组的处理方式:
- 记录数组一共有几行几列,有多少个不同值
- 把具有不同值的元素和行列以及值记录在一个小规模的数组中,从而缩小程序的规模
-
如下图 (左边是原始数组,右边是稀疏数组)
- 下标 0 的稀疏数组:记录整个数组有 6 行 7 列 8 个不为 0 的有效值
- 下标 1~8 的稀疏数组:记录每个有效值的位置。例如下标 1 中的 22 ,在数组中,下标从 0 开始计数,那么在稀疏数组中记录为 0 3 22,即为在下标为 0 的行,下标为 3 的列,记录值为 22
-
-
稀疏数组的应用
-
要求:编写五子棋游戏中,有存盘退出和续上盘的功能
-
问题分析:因为该二维数组的很多值是默认值 0,因此记录了很多没有意义的数据
-
解决方法:稀疏数组
-
代码举例
public class ArrayDemo09 { public static void main(String[] args) { //1.创建一个二维数组 11*11(11行11列) 0:没有棋子 1:黑棋 2:白棋 int[][] array1 = new int[11][11]; array1[1][2] = 1; //黑棋位于2行3列(数组从 0 计数) array1[2][3] = 2; //白棋位于3行4列(数字从 0 计数) //输出原始的数组 System.out.println("输出原始的数组"); for (int[] ints : array1) { for (int anInt : ints) { System.out.print(anInt + "\t"); } System.out.println(); } //转换为稀疏数组来保存 //1. 获取有效值的个数 int sum = 0; for (int i = 0; i < array1.length; i++) { //array1.length 取二维数组嵌套的数组个数 11行 for (int j = 0; j < array1[i].length; j++) { //array1[0].length 取遍历下标 i 的子数组中值的个数 11列 if (array1[i][j] != 0){ sum++; } } } System.out.println("有效值的个数:" + sum); //2. 创建一个稀疏数组 int[][] array2 = new int[sum+1][3]; //之所以行数为有效值个数加 1 是因为需要多加 1 行记录行总数、列总数、有效值个数。固定 3 列 //多出来的 1 行用用以下代码赋值记录行总数、列总数、有效值个数 array2[0][0] = 11; array2[0][1] = 11; array2[0][2] = sum; //遍历二维数组,将非零的值(有效值),存放在稀疏数组中 int count = 0; for (int i = 0; i < array1.length; i++) { for (int j = 0; j < array1[i].length; j++) { if (array1[i][j] != 0){ count++; //行递增 //利用行递增给稀疏数组赋值记录 array2[count][0] = i; array2[count][1] = j; array2[count][2] = array1[i][j]; } } } //3. 输出稀疏数组 System.out.println("稀疏数组"); for (int i = 0; i < array2.length; i++) { System.out.println(array2[i][0] + "\t" + array2[i][1] + "\t" + array2[i][2]); } //数组还原归位 //1. 读取稀疏数组 int[][] array3 = new int[array2[0][0]][array2[0][1]]; //根据数组默认初始化,至此一行代码已还原所有 0 值归为 //2. 给其中的元素还原它的值 for (int i = 1; i < array2.length; i++) { //还原过程中,头部记录的综合信息不需要读取,因此 i=1,从下标 1 开始 array3[array2[i][0]][array2[i][1]] = array2[i][2]; //在已记录的有效值位置还原有效值 } //3. 打印 System.out.println("打印还原的数组"); for (int[] ints : array3) { for (int anInt : ints) { System.out.print(anInt + "\t"); } System.out.println(); } } }
-
-