保持对世界的渴望与好奇,而非被浅薄的快乐左右
1. 数组的概述
概述:数组是一种数据结构,用来存储同一类型值的集合。通过一个整形下标(index,或称索引)可以访问数组中的每一个值。
特点:
- 数组是有序排列的
- 数组长度一旦确定,不能修改
- 创建数组对象会在内存中开辟一整块连续的空间
- 数组属于引用类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型
分类:
- 按照维度:一维数组、二维数组、...
- 按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素的数组
数据结构:
- 数据与数据之间的逻辑关系:集合(关系很弱)、一对一(数组里面的元素)、一对多(二叉树)、多对多(社交网络)
- 数据的存储结构:
- 线性表:顺序表(比如:数组)、链表、栈、队列、
- 树形结构:二叉树、树形结构
2. 数组的声明
2.1 Java 虚拟机的内存划分
为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

区域名称 | 作用 |
---|---|
虚拟机栈 |
用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度 的各种基本数据类型、对象引用,方法执行完,自动释放。 |
堆内存 |
存储对象(包括数组对象),new来创建的,都存储在堆内存。 |
方法区 |
存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。 |
本地方法栈 | 当程序中调用了native的本地方法时,本地方法执行期间的内存区域 |
程序计数器 | 程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址 |
注:摘自康师傅 Java 课件与电子教材
2.2 一维数组
在声明数组变量时,需要指定数组类型(数据元素类型紧跟 [] )和数组变量的名称
java
// 只声明了变量 a
int[] a;
// 静态初始化:数组的初始化和赋值同时进行
a = new int[]{1001, 1002, 1003, 1004};
// 动态初始化:数组初始化和元素赋值分开操作
String[] names = new String[5];
// Java 中,提供了一种创建数组对象并同时提供初始值的简写形式
// 注意,这个语法中不需要使用 new,甚至不需要指定长度
int[] smallPrimes = {2, 3, 4, 5, 6};
// 也可以使用下面两种形式定义一个数组变量,第一种风格使用的多
// 因为它可以将类型 int[](整形数组) 与变量明清晰的分开
int[] b;
int c[];
错误的方式
java
//int[] arr1 = new int[];
//int[5] arr2 = new int[5];
//int[] arr3 = new int[3]{1,2,3};
数组元素调用、获取数组 length、两种方式遍历数组元素
java
public class ArrayTest1 {
public static void main(String[] args) {
// 动态初始化:数组初始化和元素赋值分开操作
String[] names = new String[5];
// 通过角标的方式调用
names[0] = "张三";
names[1] = "李四";
names[2] = "王五";
names[3] = "赵六";
names[4] = "孙七";
// 获取数组元素的长度:length
System.out.println(names.length);
// 遍历数组 for 循环
for (int i = 0; i < names.length; i++) {
System.out.print(names[i] + " ");
}
// 换行一下
System.out.println();
// 通过 for each 循环进行遍历数组
for (String element : names) {
System.out.print(element + " ");
}
}
}
默认初始化值
- 数组元素是整型:0
- 数组元素是浮点型:0.0
- 数组元素是char型:0或'\u0000',而非'0'
- 数组元素是boolean型:false
- 数组元素是引用数据类型:null

一维数组内存解析大致过程

2.3 二维数组
二维数组元素获取、获取数组 length、两种方式遍历数组元素
java
public class ArrayTest2 {
public static void main(String[] args) {
// 静态初始化
int[][] arr1 = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {22, 12, 9}};
// 动态初始化1
String[][] arr2 = new String[3][2];
// 动态初始化2
String[][] arr3 = new String[3][];
//也是正确的写法:
int arr4[][] = new int[][]{{1, 2, 3}, {4, 5, 9, 10}, {6, 7, 8}};
int[] arr5[] = new int[][]{{1, 2, 3}, {4, 5, 9, 10}, {6, 7, 8}};
int[][] arr6 = {{1, 2, 3}, {4, 5}, {6, 7, 8}};//类型推断
// 获取数组指定位置的元素
System.out.println(arr1[0][2]); // 3
// System.out.println(arr1[0][3]); // ArrayIndexOutOfBoundsException 数组索引越界异常
System.out.println(arr2[1][1]); // null
// System.out.println(arr3[1][1]); // NullPointerException 空指针异常
// 数组长度
System.out.println(arr1.length); // 4
System.out.println(arr1[1].length); // 3
// 遍历二维数组
for (int i = 0; i < arr1.length; i++) {
for (int j = 0; j < arr1[i].length; j++) {
System.out.print(arr1[i][j] + " ");
}
System.out.println();
}
// 换行
System.out.println();
for (int[] e : arr1) {
for (int j : e) {
System.out.print(j + " ");
}
System.out.println();
}
}
}
错误的方式
java
//String[][] arr4 = new String[][4];
//String[4][3] arr5 = new String[][];
//int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};
数组元素的默认初始化值
-
针对于初始化方式一:比如:int[][] arr = newint[4][3];
- 外层元素的初始化值为:地址值
- 内层元素的初始化值为:与一维数组初始化情况相同
-
针对于初始化方式二:比如:int[][] arr = newint[4][];
- 外层元素的初始化值为:null
- 内层元素的初始化值为:不能调用,否则报错
二维数组内存解析大致过程

3. 数组的常见算法
3.1 数组元素的赋值
杨辉三角

java
public class ArrayEver {
public static void main(String[] args) {
// 1. 新建二维数组,动态初始化
int[][] yangHui = new int[10][];
// 2. 为二维数组赋值操作
for (int i = 0; i < yangHui.length; i++) {
// 第一行有 1 个元素,总共 10 行
yangHui[i] = new int[i + 1];
// 2.1 为首末行元素赋值 1
yangHui[i][0] = yangHui[i][i] = 1;
// 2.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[] ints : yangHui) {
for (int anInt : ints) {
System.out.print(anInt + " ");
}
System.out.println();
}
/*
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1
*/
}
}
3.2 针对数值型的数组
java
/**
* 针对数值类型的基本操作
* 定义一个 int 型的一维数组,包含 10 个元素,分别赋值一些随时数,所有随机数都是两位数
* 求:数值型数组元素的最大值、最小值、平均数、总和
* [10, 99] 公式:(int)(Math.random() * (99 - 10 + 1) + 10)
*
*/
public class ArrayEver1 {
public static void main(String[] args) {
int[] arr = new int[10];
// 数组赋值操作
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * (99 - 10 + 1) + 10);
}
// 遍历
for (int i : arr) {
System.out.print(i + " ");
}
// 换行
System.out.println();
// 求数组元素的最大值
int maxValue = arr[0];
for (int i : arr) {
if(maxValue < i){
maxValue = i;
}
}
System.out.println("最大值:" + maxValue);
// 求数组元素的最小值
int minValue = arr[0];
for (int i : arr) {
if(maxValue > i){
maxValue = i;
}
}
System.out.println("最小值:" + minValue);
// 求数组元素的总和
double sum = 0;
for (int i : arr) {
sum += i;
}
System.out.println("总和:" + sum);
// 求数组元素的平均数
double avgValue = sum / arr.length;
System.out.println("平均数:" + avgValue);
}
}
Math.random()方法
在 Java 中主要提供了两种方式产生随机数,分别为调用 Math 类的 random()方法 和 Random 类提供的产生各种数据类型随机数的方法。
在 Math 类中存在一个 random() 方法,用于产生随机数字,这个方法默认生成大于等于 0.0 且小于 1.0 的 double 型随机数;即 0 <= Math.random() < 1.0,虽然 Math.random() 方法只可以产生 0 ~ 1 之间的double 型数字,其实只要在 Math.random() 语句上稍加处理,就可以产生任意范围的随机数。
(int)(Math.Random() * n ) → 返回大于等于 0 且小于 n 的随机数
m + (int)(Math.Random() * n ) → 返回大于等于 m 且小于 m+n(不包括m+n)的随机数
使用 Math 类的 random() 方法也可以随机生成字符,可以使用如下代码生成 a ~ z 之间的字符。
(char)('a' + Math.random() * ('z' - 'a' + 1));
上述表达式可以求出更多的随机字符,如 A ~ Z 之间的随机字符,进而推理出求任意字符之间的随机字符,可以使用下列语句表示。
(char)(char1 + Math.random() * (cha2 - cha1 + 1));
3.3 数组的赋值与复制
赋值
java
public class ArrayTest3 {
public static void main(String[] args) {
int[] array1, array2;
array1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
// array1 和 array2 地址相同,都指向了堆空间同一个数组实体
array2 = array1;
for (int i = 0; i < array2.length; i++) {
if (i % 2 == 0) {
array2[i] = i;
}
}
// 打印结果相同
for (int i : array1) System.out.print(i + " "); // 0 3 2 7 4 13 6 19
System.out.println();
for (int i : array2) System.out.print(i + " "); // 0 3 2 7 4 13 6 19
}
}
简单理解如下图

复制,目前复制 Java 数组的复制,可以使用 System.arraycopy() 方法,但在这里,先用最简单的方式描述
java
public class ArrayTest4 {
public static void main(String[] args) {
int[] array1, array2;
array1 = new int[]{2, 3, 5, 7, 11, 13, 17, 19};
// 数组的复制
array2 = new int[array1.length];
for (int i = 0; i < array2.length; i++) {
array2[i] = array1[i];
}
array2[0] = 33;
for (int i : array1) System.out.print(i + " "); // 2 3 5 7 11 13 17 19
System.out.println();
for (int i : array2) System.out.print(i + " "); // 33 3 5 7 11 13 17 19
}
}
简单理解如下图

3.4 数组元素的反转
数组的反转,主要是找个临时变量 temp 进行替换操作即可
java
public class ArrayTest5 {
public static void main(String[] args) {
String[] arr = new String[]{"SS", "QQ", "YY", "XX", "TT", "KK", "EE", "GG", "KK"};
// 数组的反转,方法 1
for (int i = 0; i < arr.length / 2; i++) {
String temp = arr[i];
arr[i] = arr[arr.length - i -1];
arr[arr.length - i -1] = temp;
}
for (String i : arr) System.out.print(i + " "); // KK GG EE KK TT XX YY QQ SS
}
}
3.5 数组中指定元素的查找
线性查找
java
/**
* 元素的查找:线性查找
*
*/
public class ArrayTest6 {
public static void main(String[] args) {
String[] arr = new String[]{"SS", "QQ", "YY", "XX", "TT", "KK", "EE", "GG", "KK"};
String dest = "YY";
boolean isFlag = true;
// 线性查找:通过遍历的方式,一个一个数据进行比较,查找。具体普遍适用性
for (int i = 0; i < arr.length; i++) {
if(dest.equals(arr[i])){
System.out.println("找到了指定元素,索引位置为: " + i);
isFlag = false;
break;
}
}
if(isFlag) System.out.print("很遗憾,没有找到元素");
}
}
二分查找
java
/**
* 二分查找,前提是,所要查找的数组必须有序
* 思路:每次比较中间值,折半的方式检索
*
*/
public class ArrayTest7 {
public static void main(String[] args) {
int[] arr2 = new int[]{-12, -8, 2, 12, 33, 35, 78, 89, 99, 112, 333, 456};
int dest = 33;
int head = 0; // 初始的首索引
int end = arr2.length - 1; // 初始的末索引
boolean isFlag = true;
while (head <= end) {
int minddle = (head + end) / 2;
if (dest == arr2[minddle]) {
System.out.println("找到了指定元素,索引位置为:" + minddle);
isFlag = false;
break;
} else if (arr2[minddle] > dest) {
end = minddle - 1;
} else {
head = minddle + 1;
}
}
if (isFlag) System.out.print("很遗憾,没有找到元素");
}
}
3.6 数组的排序算法
「十大排序算法」
- 选择排序:
- 直接选择排序、堆排序
- 交换排序:
- 冒泡排序、快速排序
- 插入排序:
- 直接插入排序、折半插入排序、希尔排序
- 归并排序
- 桶排序
- 基数排序
一些理解内容
- 衡量排序算法的优劣:时间复杂度、空间复杂度、稳定性
- 排序的分类:内部排序 与 外部排序(需要借助磁盘)
- 不同排序算法的性能对比

算法的五大特性
输入(Input) | 有 0 个或多个输入数据,这些输入必须有清楚的描述和定义 |
---|---|
输出(Output) | 至少有 1 个或多个输出结果,不可以没有输出结果 |
有穷性(有限性,Finiteness) | 算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成 |
确定性(明确性,Definiteness) | 算法中的每一步都有确定的含义,不会出现二义性 |
可行性(有效性,Effectiveness) | 算法的每一步都是清楚且可行的,能让用户用纸笔计算而求出答案 |
3.6.1 冒泡排序
冒泡排序的基本思想:通过对待排序序列从前向后,依次比较相邻元素的排序码,若发现逆序则交换,使排序码较大的元素逐渐从前部移向后部。
因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序, 因此要在排序过程中设置一个标志 swap 判断元素是否进行过交换。从而减少不必要的比较。

java
public class ArrayTest8 {
public static void main(String[] args) {
int[] arr = new int[]{2, 44, 11, 23, 1, 6, 47, 89, 56, 62, 38, 68, 46, 35, 99};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for(int i : arr) System.out.print(i + " ");
}
}
3.6.2 快速排序
这里暂时先不写,后续补充,目前冒泡,快速排序是必须掌握的内容
5. Arrays 工具类的使用
定义在 java.util 包下,Arrays 工具类提供了很多操作数组的方法,更多方法可参考 API 文档
boolean equals(int[] a, int[] b) | 判断两个数组是否相等 |
---|---|
String toString(int[] a) | 输出数组信息 |
void fill(int[] a,int val) | 将指定值填充到数组之中 |
void sort(int[] a) | 对数组进行排序 |
int binarySearch(int[] a,int key) | 对排序后的数组进行二分法检索指定的值 |
java
import java.util.Arrays;
/**
* Arrays 工具类的使用
*
*/
public class ArrayTest9 {
public static void main(String[] args) {
int[] arr1 = new int[]{1, 2, 3, 4};
int[] arr2 = new int[]{9, 5, 3, 7};
// 判断两个数组是否相等
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals); // false
// 输出数组信息
System.out.println(Arrays.toString(arr1)); // [1, 2, 3, 4]
// 将指定的值填充到数组中
Arrays.fill(arr1, 10);
System.out.println(Arrays.toString(arr1)); // [10, 10, 10, 10]
// 对数组进行排序
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2)); // [3, 5, 7, 9]
// 对排序后的 arr2 进行二分法检索指定的值
int index = Arrays.binarySearch(arr2, 5);
if (index >= 0) {
System.out.println(index); // 1
} else {
System.out.println("未找到");
}
}
}
6. 数组使用中的常见异常
java
/**
* 数组中常见异常:
* 1. ArrayIndexOutOfBoundsException 数组角标越界异常
* 2. NullPointerException 空指针异常
*
*/
public class ArrayTest10 {
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
// System.out.println(arr[9]); 触发 ArrayIndexOutOfBoundsException 异常
// NullPointerException 空指针异常
int[] arr1 = new int[]{1, 2, 3};
// 情况 1
// arr1 = null;
// System.out.println(arr1[0]);
// 情况 2
// int[][] arr2 = new int[5][];
// System.out.println(arr2[0][0]);
// 情况 3
// String[] arr3 = new String[]{"aa", "bb", "cc"};
// arr3[0] = null;
// System.out.println(arr3[0].toString());
}
}
提示:一旦程序出现异常,未处理时,就终止执行。