文章目录
- 一、数组的核心特性
- 二、数组的定义与初始化
-
- [2.1 声明数组](#2.1 声明数组)
- [2.2 初始化数组](#2.2 初始化数组)
- 三、数组的基本操作
-
- [3.1 访问元素](#3.1 访问元素)
- [3.2 获取数组长度](#3.2 获取数组长度)
- [3.3 遍历数组](#3.3 遍历数组)
- [3.4 数组的复制](#3.4 数组的复制)
- [3.5 数组的排序](#3.5 数组的排序)
- [3.6 数组的查找](#3.6 数组的查找)
- 四、数组的内存分析
- 五、二维数组
-
- [5.1 二维数组的定义与初始化](#5.1 二维数组的定义与初始化)
- [5.2 二维数组的访问与遍历](#5.2 二维数组的访问与遍历)
- [5.3 数组实用工具类](#5.3 数组实用工具类)
一、数组的核心特性
数组是java中存储固定大小、相同类型元素的容器,是最基础的数据结构之一,核心特性是长度不可变,索引从0开始。下面小编从定义、使用、内存、操作、多维数组等维度进行讲解。
- 类型统一:只能存储同一种数据类型(基本类型/引用类型),比如int数组只能存int,String数组只能存String。
- 长度固定:数组一旦创建,长度无法进行修改。如果想要对数组进行扩容只能新建数组并把元素复制到新数组中。
- 索引访问:通过数组名[索引]快速访问元素,索引范围为0 ~ 数组长度-1,越界会抛ArrayIndexOutOfBoundException。
- 内存连续:数组元素在内存中连续存储,这是其访问效率高的核心原因。
- 引用类型:数组是引用类型,数组中的数据存储在堆内存,变量存储的是数组的内存地址(引用)。
二、数组的定义与初始化
2.1 声明数组
声明数组有两种方式
java
//方式1:推荐,更符合Java规范
数组类型[] 数组名;
//方式2:c语言风格,java也支持但不推荐
数据类型 数组名[];
//示例
int[] arr1; //声明int类型数组
String[] arr2; //声明String类型数组
double[] arr3; //声明double类型数组
注意:声明不分配内存,此时数组名是null,不能直接进行使用。
2.2 初始化数组
初始化数组有三种方式
- 动态化初始(指定长度,元素默认赋值):创建数组的时候指定长度,元素会被赋予默认值(基本类型:0/0.0/false;引用类型:null)
java
//语法:数据类型[] 数组名 = new 数组类型[长度];
public static void main(String[] args) {
int[] arr = new int[5]; //长度为5.元素默认[0,0,0,0,0]
String[] strArr = new String[3]; //元素默认[null,null,null]
boolean[] boolArr = new boolean[2]; //元素默认[false,false]
}
- 静态初始化(指定元素,长度有元素个数进行决定):创建数组的时候直接进行赋值,长度自动等于元素个数,无需手动指定。
java
public static void main(String[] args) {
//标准写法:数组类型[] 数组名 = new 树蕨类型[]{元素1,元素2,...};
int[] arr1 = new int[]{1,2,3,4,5};//长度5,元素[1,2,3,4,5]
//错误写法,如果使用静态初始化,不可以在赋值的时候同时指定数组的大小,数组的大小由赋值的个数决定
//int[] arr2 = new int[6]{1,2,3,4,5};
//简化写法:数组类型[] 数组名 = {元素1,元素2,....};
int[] arr3 = {1,2,3,4,5};//长度5,元素[1,2,3,4,5]
}
注意:简化写法{}只能在声明时使用,以下写法错误:
java
int[] arr;
arr = {1,2,3};//编译报错!!!
arr = new int[]{1,2,3};//正确,必须用new
- 匿名数组(无变量名,临时使用):直接创建数组并使用,无需赋值给变量,常用于方法传参,
java
//调用的时候传入匿名数组
public static void printArray(int[] arr) {
for (int num : arr) {
System.out.print(num + " ");
}
}
public static void main(String[] args) {
printArray(new int[]{1,2,3,4,5});
}
三、数组的基本操作
3.1 访问元素
数组中访问元素通过索引进行访问
java
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
//访问第一个元素,索引为0
int first = arr[0]; //输出1
//访问最后一个元素,索引为长度-1
int last = arr[arr.length - 1]; //输出5
//修改元素
arr[2] = 999; //数组变为[1,2,999,4,5]
for (int num : arr) {
System.out.print(num + " ");
}
}
注意:索引不可越界,如arr[ - 1]或者 arr[ 5] 会导致报错,抛出ArrayIndexOutOfBoundException。
3.2 获取数组长度
数组有length属性(注意:不是方法,无括号),返回数组的元素个数。
java
public static void main(String[] args) {
int[] arr = {1,2,3,4};
System.out.println(arr.length); //4
}
区分:string中的length()是方法存在括号,数组中的length是属性(无括号)。
3.3 遍历数组
遍历数组有三种方式
- ( 1 ) 普通for循环(最常用,支持索引操作)
java
public static void main(String[] args) {
int[] arr = {1,2,3,4};
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
}
- ( 2 ) 增强for循环(foreach,仅遍历元素,无索引)
java
public static void main(String[] args) {
int[] arr = {1,2,3,4};
for (int num : arr) {
System.out.print(num + " ");
}
}
注意:增强for循环无法修改数组元素,仅读取,想修改需使用普通for循环
- ( 3 ) Arrays工具类遍历(toString方法)
java.util.Arrays是数组操作工具类,toString()可直接打印数组内容(避免手动遍历)
java
public static void main(String[] args) {
int[] arr = {1,2,3,4};
System.out.println(Arrays.toString(arr));
}
输出:

3.4 数组的复制
在前面说过,数组的一个特性是数组长度不可变,所以数组的复制是实现"扩容/缩容"的核心手段
- ( 1 ) System.arraycopy(底层方法,效率最高)
语法:System.arraycopy(源数组,源起始索引,目标数组,目标起始索引,复制的长度)
java
public static void main(String[] args) {
int[] arr = {1,2,3,4};
int[] arr2 = new int[arr.length];
System.arraycopy(arr,0,arr2,0,arr.length); //从arr索引为0的位置开始复制同arr数组同样长度个元素到arr2
System.out.println(Arrays.toString(arr2));
//此时验证以下是否是复制的,而不是引用指向原来的数组arr
System.out.println("----------------------");
arr[1] = 999;
System.out.println(Arrays.toString(arr2));
}
- ( 2 ) Array.copyOf(常用,自动创建新的数组)
java
public static void main(String[] args) {
int[] arr = {1,2,3,4};
int[] newArr = Arrays.copyOf(arr,5); //扩容到5,新元素默认0 -> [1,2,3,4,0]
System.out.println(Arrays.toString(newArr));
int[] newArr2 = Arrays.copyOf(arr,2); //缩容到2 -> [1,2]
System.out.println(Arrays.toString(newArr2));
}
输出:

- ( 3 )Array.copyOfRange(复制指定范围)
语法:Array.copyOfRange(源数组,起始索引,结束索引),遵循左闭右开,即 [ 起始索引,结束索引)
java
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int[] newArr = Arrays.copyOfRange(arr,0,3); //复制索引0~2 -> [1,2,3]
System.out.println(Arrays.toString(newArr));
}
- ( 4 ) for循环赋值进行复制
java
public static void main(String[] args) {
int[] arr = {1,2,3,4};
int[] newArr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
newArr[i] = arr[i];
}
System.out.println(Arrays.toString(newArr));
}
3.5 数组的排序
Arrays.sort()可对数组进行升序排序(基本类型:快速排序;引用类型:归并排序)
java
public static void main(String[] args) {
//基本类型排序
int[] arr1 = {5,3,1,4,2};
Arrays.sort(arr1);
System.out.println(Arrays.toString(arr1)); //[1, 2, 3, 4, 5]
//引用类型排序(String 按字典序)
String[] arr2 = {"banana","apple","orange"};
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2)); //[apple, banana, orange]
}
字典序(Lexicographical Order),简单说就是按照我们查字典的规则来排列字符 / 字符串 / 序列的顺序,核心是从左到右逐个比较元素,先出现不同元素的位置,谁的元素 "更小",整个序列就排在前面;若前面元素都相同,短的序列排在前面。
字符串字典序的比较步骤:
- 从左到右逐个字符对比,找到第一个不相同的字符;
- 该位置字符ASCII 码更小的字符串,整体排在前面;
- 若一个字符串是另一个的前缀(前面字符全相同),长度更短的排在前面。
示例:
apple < apply:前 4 个字符appl都相同,第 5 位e(101) < y(121)
abc < abcd:abc是abcd的前缀,更短所以排前面
123 < A123:第一位1(49) < A(65)
Abc < abc:第一位A(65) < a(97)
zoo < zoom:前 3 个字符zoo相同,zoo更短排前面
注意:自定义排序需实现Comparable接口或传入Comparator比较器
- ( 2 )冒泡排序
java
public static void swap(int[] arr,int x,int y) {
int tmp = arr[x];
arr[x] = arr[y];
arr[y] = tmp;
}
public static void MybubbleSort(int[] arr) {
boolean flg = true;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if(arr[j] > arr[j+1]) {
swap(arr,j,j+1);
flg = false; //如果发生交换就说明后面的不有序
}
}
}
//当上面如果不存在交换的时候说明数组已经有序了,后面则不需要进行交换,直接返回
if(flg) {
return;
}
}
public static void main(String[] args) {
int[] arr = {7,5,9,10,3,4,2,1,6,8};
MybubbleSort(arr);
System.out.println(Arrays.toString(arr));
}
3.6 数组的查找
- ( 1 )线性查找:遍历所有元素,适合无序数组
java
public static int find(int[] arr,int targe) {
for (int i = 0; i < arr.length; i++) {
if(arr[i] == targe) {
return i; //找到返回索引
}
}
return -1; //未找到返回-1
}
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
int targe = 3;
int ret = find(arr,targe);
if(ret == -1) {
System.out.println("找不到");
}else {
System.out.println("找到了,下标为:" + ret);
}
}
- ( 2 )二分查找:效率高(O(logn)),必须先排序,否则结果无法预测
java
//自己自定义的方法
public static int MyBinarySearch(int[] arr,int targe) {
int left = 0;
int right = arr.length - 1;
int mid = (left + right) / 2;
while (left <= right) {
if(arr[mid] > targe) {
right = mid - 1;
}else if(arr[mid] < targe) {
left = mid + 1;
}else {
return mid;
}
mid = (left + right) / 2;
}
return -1;
}
public static void main(String[] args) {
int[] arr = {1,2,3,4,5,6,7,8,9,10};
int targe = 1;
int ret = MyBinarySearch(arr,targe);
int ret2 = Arrays.binarySearch(arr,10);
System.out.println(ret2); //java中Arrays类的方法
if(ret == -1) {
System.out.println("找不到");
}else {
System.out.println("找到了,下标为:" + ret);
}
}
四、数组的内存分析
数组是引用类型,内存分配为栈内存和堆内存:
- 栈内存:存储数组变量(引用),比如int[] arr中的arr,存储的是数组在堆中的内存地址。
- 堆内存:存储数组的实际元素,数组对象本身在堆中分配连续空间。
示例:数组创建的内存过程
java
int[] arr = new int[3];
arr[0] = 10;
arr[1] = 20;
- 栈中创建变量arr,初始为null;
- 堆中分配3个连续int空间(默认值为0),返回内存地址给arr;
- 通过arr[0]、arr[1]修改堆中元素,最终堆中元素为[10,20,0]。
数组作为方法参数时,传递的是内存地址(引用),方法内修改数组元素,会影响原来数组
java
public static void changeArray(int[] arr) {
arr[0] = 100; //修改堆中元素
}
public static void main(String[] args) {
int[] arr = {1,2,3};
changeArray(arr);
System.out.println(arr[0]); //原数组被修改
}
!注意:如果方法内重新给数组变量赋值(arr = new int[ 5 ]),只是改变了局部变量的引用,不会影响原来数组
java
public static void changeArray(int[] arr) {
arr = new int[5]; //新创建一个数组对象,让arr引用指向新的数组对象
arr[0] = 100; //并不会影响原来数组
}
public static void main(String[] args) {
int[] arr = {1,2,3};
changeArray(arr);
System.out.println(arr[0]); //原数组未被修改
}
五、二维数组
二维数组的本质就是存储一维数组地址的数组。

5.1 二维数组的定义与初始化
- ( 1 )动态初始化
java
public static void main(String[] args) {
//语法1:指定行数和列数(规则二维数组)
int[][] arr1 = new int[3][2]; //3行2列,元素默认为0
//语法2:仅指定行数,列数动态分配
int[][] arr2 = new int[2][]; //2行,列数未分配
arr2[0] = new int[2]; //第一行2列
arr2[1] = new int[3]; //第二行3列
}
- ( 2 )静态初始化
java
public static void main(String[] args) {
//标准写法
int[][] arr1 = new int[][]{{1,2},{3,4,5}};
//简化写法
int[][] arr2 = {{1,2},{3,4,5}};
}
5.2 二维数组的访问与遍历
- ( 1 )访问元素
java
public static void main(String[] args) {
int[][] arr = {{1,2},{3,4,5}};
int num = arr[0][1]; //访问第一行第二列的元素 -> 2
arr[1][2] = 999; //修改第二行第三列元素 -> 5变为999
}
- ( 2 )遍历元素
java
public static void main(String[] args) {
int[][] arr = {{1,2},{3,4,5}};
//普通for循环
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[i].length; j++) {
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
//增强for循环
for(int[] row : arr) {
for (int line : row) {
System.out.print(line + " ");
}
System.out.println();
}
}
- ( 1 )打印元素
Arrays.toString() 仅打印一维数组,二维数组需用Arrays.deepToString()
java
public static void main(String[] args) {
int[][] arr = {{1,2},{3,4,5}};
System.out.println(Arrays.deepToString(arr));
}
5.3 数组实用工具类
Java提供Arrays工具类,封装了数组的常用操作,无需手动实现
java
public static void main(String[] args) {
int[] arr = {5,3,1,4,2};
//1.排序
Arrays.sort(arr);
//2.二分查找
int index = Arrays.binarySearch(arr,3);
//3.复制
int[] newArr = Arrays.copyOf(arr,10);
//4.填充:所有元素赋值为指定值
Arrays.fill(arr,0); //arr变为【0,0,0,0,0】
//5.比较两个数组是否相等
int[] arr1 = {1,2,3};
int[] arr2 = {1,2,3};
boolean isEqual = Arrays.equals(arr1,arr2);//true
//6.打印数组
System.out.println(Arrays.toString(arr));
}