Java基础入门--数组指南
目录
一.为什么要使用数组?
在学习数组前,我们通常使用单个变量存储数据。假如要存储5名学生的考试成绩,就需要创建5个变量,但如果是100名学生呢?
数组的诞生就是解决这个问题--它可以存储多个相同类型的数据,在内存中占据一段连续的空间。你可以把它想像成一个整齐的“车库”或“储物柜”,每个格子大小相同,且紧密相连。
二.数组的创建和初始化
2.1数组的创建
T [] name =new T [N];
T:数组中元素类型
N:数组的长度(元素个数)
示例:
int [] arr1=new int[10]; //创建一个可以容纳10个int类型元素的数组
double [] arr1=new double[10]; //创建一个可以容纳10个double类型元素的数组
String [] arr1=new String[5]; //创建以一个可以容纳5个字符串元素的数组
2.2数组的初始化
1.动态初始化
创建时只指定长度,数组元素会被赋予该类型的默认值:
int[] arr=new int[10] //所有元素默认为0
2.静态初始化
创建时直接给出具体的数据内容,长度由编译器自动推导:
//完整写法
int[] arr1=new int [] {1,2,3,4,5,6,7,8,9};
//简化写法
int[] arr2={1,2,3,4,5,6,7,8,9};
注意:静态初始化时,{ }中的数据类型必须和数组类型相同。简化写法只能在定义时同时初始化,不能先声明再赋值(将简化写法分两步走不能行)
2.3数组的默认值
如果没有显示初始化,数组元素会有默认值:
| 类型 | 默认值 |
| byte,short,int long | 0 |
| float,double | 0.0 |
| char | '\u0000'(空字符) |
| bollean | false |
| 引用类型(String) | null |
三. 深入理解数组:JVM内存和引用类型
3.1JVM内存分布简析:
Java虚拟机(JVM)将内存划分为几个区域,我们重点关注两个:
堆(Heap):存放所有new出来的对象和数组。堆中的数据只要还有指向它的就不会被销毁。
虚拟机栈(JVM Stack):存放局部变量和方法调用的信息,每个方法执行时都会在虚拟机栈上创建一个栈帧,方法结束后栈帧被销毁。
3.2基本类型vs引用类型
public static void main(String[] args){
int a=10; //基本类型变量,栈中直接存值10
int[] arr={1,2,3}; //引用类型变量,栈中存地址(指向堆中的数组)
}
基本类型变量:变量空间里存放的就是值本身。
引用类型变量:变量空间存放的是对象在堆中的地址(可以理解为一个指向)。通过这个地址,引用变量就可以操作对象。它类似于c语言的指针,但更简单更安全。
当数组作为参数传递时,传递的是地址,所以在方法内部修改数组会影响原数组:
public static void main(String[] args){
int [] arr={1,2,3};
func(arr);
System.out.println(arr[0]); //输出10,原数组被修改了
}
public static void func(int[] a){
a[0]=10;
}
这种设计可以避免对整个数组进行拷贝
3.3null含义
null表示“空引用”,即该引用不指向任何对象。
int[] arr=null;
System.out.println(arr[0]); //抛出NullPointerException
操作null指针会抛空指针异常
四.数组的基本使用
4.1元素访问与下标
数组通过下标来访问元素,下标从0开始,最大值为数组长度-1
int[] array = {10, 20, 30, 40, 50};
System.out.println(array[0]); // 输出 10
array[2] = 100; // 修改第三个元素
System.out.println(array[2]); // 输出 100
下标越界错误:
System.out.println(array[5]); // 只有5个元素,下标最大为4 → ArrayIndexOutOfBoundsException
4.2数组遍历
遍历是指将数组所有元素都访问一遍,手动逐个打印效率低下且不可维护,我们通常使用循环。
1. 使用普通for循环
int[] array = {10, 20, 30, 40, 50};
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
array.length可以获取数组长度
2.增强for训话(for-each)
for (int x : array) {
System.out.println(x);
}
for-each语法更简洁,能有效避免下标越界。但是循环变量x只是一个临时拷贝,因此无法修改数组元素值
五.常用操作和经典算法
5.1数组转字符串(快速打印)
直接打印数组对象会输出哈希码,需要使用Arrays.toString():
import java.util.Arrays;
int[] arr = {1, 2, 3, 4, 5, 6};
System.out.println(Arrays.toString(arr));
// 输出:[1, 2, 3, 4, 5, 6]
5,2数组拷贝(浅拷贝--引用赋值)
int[] arr={1,2,3};
int[] newArr=arr; //newArr和arr指向同一个数组
newArr[0]=100;
System.out.println(arr[0]); //输出100,arr数组也被改了
真正的拷贝:使用Arrays.copyOf() 创建新的数组并拷贝内容:
int[] arr={1,2,3};
int[] newArr =Arrays.copyOf(arr,arr.length);
newArr[0]=100;
System.out.println(arr[0]); //输出1
System.out.println(newArr[0]); //输出100
Arrays.copyOfRange(arr,from,to):指定拷贝范围,arr.clone() 克隆方法
5.3查找元素
1.顺序查找(适用于无序数组)
public static int find(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) return i;
}
return -1; // 未找到
}
2.二分查找(适用于有序数组)
核心思想:每次取中间元素和目标值比较,如果目标值小,则去左半侧,大则去右半侧。
public static int binarySearch(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (target < arr[mid]) {
right = mid - 1; // 去左半区
} else if (target > arr[mid]) {
left = mid + 1; // 去右半区
} else {
return mid; // 找到了
}
}
return -1; // 未找到
}
java内置了二分查找:Arrays.binarySearch(ar,target)---要求数组已排序
5.4数组排序
冒泡排序:时间复杂度为O(n**2),性能较低。
public static void bubbleSort(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j+1]) {
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
使用内置排序:Arrays.sort(arr);
5.5数组逆序
public static void reverse(int[] arr) {
int left = 0, right = arr.length - 1;
while (left < right) {
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
left++;
right--;
}
}
5.6数组填充
int[] arr = new int[5];
Arrays.fill(arr, 10);
System.out.println(Arrays.toString(arr)); // [10, 10, 10, 10, 10]
六.二维数组
6.1规则二维数组
二维数组本质上是一个一维数组,每个元素又是一个一维数组。
// 静态初始化
int[][] matrix = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 动态初始化(2行3列)
int[][] arr = new int[2][3];
arr.length:行数(外层数组的长度)
arr[row].length:第row行的列数
遍历(嵌套循环)
for (int row = 0; row < matrix.length; row++) {
for (int col = 0; col < matrix[row].length; col++) {
System.out.printf("%d\t", matrix[row][col]);
}
System.out.println(); // 换行
}
快速打印二维数组:
System.out.println(Arrays.deepToString(matrix));
// 输出:[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
6.2不规则二维数组(锯齿数组)
java的二维数组非常灵活,不必要求每行的列数相同。
int[][] jagged = new int[2][]; // 先指定行数,列数暂不指定
jagged[0] = new int[3]; // 第0行有3列
jagged[1] = new int[5]; // 第1行有5列
// 之后可以像普通二维数组一样赋值
jagged[0][0] = 10;
这种结果在内存中是这样的:
Row 0: [ ][ ][ ] Row 1: [ ][ ][ ][ ][ ]
6.3二维数组的内存逻辑
1.外层int[ ][ ] arr是一个引用,指向堆中的一个一维数组(该数组的元素类型是int[ ]引用)。
2.这个一维数组的每个元素又分别指向另一个存放具体数据的一维数组。
这种设计使得java的多维数组非常灵活,但每个内层数组都是独立的对象,在内存不一定连续存储
总结:
1.数组是存储相同类型元素的连续内存空间,支持随机访问。
2.数组是引用类型,变量存的是堆中的地址。
3.二维数组本质是数组的数组,支持不规则结构(锯齿结构)
更多推荐




所有评论(0)