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