Java基础全套教程(四)—— 数组与基础数据结构
Java基础全套教程(四)—— 数组与基础数据结构
前面章节我们掌握了Java流程控制、方法、递归的核心逻辑,具备了基础编程思维。本章将学习Java最核心的基础数据结构——数组。数组是存储批量数据的容器,是后续集合、算法、框架开发的底层基础,几乎所有业务代码和算法题目都会用到数组。
同时本章搭配冒泡排序、二分查找两大经典入门算法,结合可视化学习工具,帮大家建立基础数据结构与算法思维,为后续高阶编程铺路。
本章学习目标
-
理解数组的核心概念、四大特性,区分基本类型数组与引用类型数组;
-
熟练掌握数组三种初始化方式、数组遍历、数组拷贝等常用操作;
-
精通 java.util.Arrays 工具类所有常用方法,解决数组日常开发场景;
-
掌握多维数组的内存结构与使用场景,实现表格数据存储;
-
掌握JavaBean结合数组存储结构化数据的开发规范;
-
熟练手写基础冒泡排序、优化冒泡排序、二分查找三大经典算法。
4.1 数组核心概念与四大特点
4.1.1 什么是数组
数组是相同数据类型的有序、连续存储集合。数组中每一条数据称为元素,所有元素按照存入顺序有序排列,开发者可通过**索引(下标)**精准访问、修改对应元素。
数组索引规则:从0开始,最大索引为 数组长度-1,超出范围会抛出数组越界异常。
4.1.2 数组四大核心特点(面试重点)
特点1:长度固定不可变
数组在内存中是连续空间,一旦初始化完成、分配内存,数组长度永久固定,无法扩容或缩容。日常集合扩容本质是新建新数组+拷贝旧数组数据实现。
特点2:元素类型统一
同一个数组中,所有元素必须是完全相同的数据类型,不支持整型、字符串、对象等混合存储,保证数据存储的规整性。
特点3:适配所有数据类型
数组的元素类型无限制,既可以存储byte、int、double等基本数据类型,也可以存储自定义对象、字符串等引用数据类型。
特点4:数组属于引用类型
数组本身是Java中的对象,继承自Object类,存储在堆内存中;数组变量是引用变量,存储在栈内存,指向堆内存中的数组对象,数组元素等价于对象的属性。
4.2 数组声明与初始化
4.2.1 数组声明语法(一维数组)
Java提供两种数组声明格式,推荐使用第一种,更符合开发规范:
// 规范写法:类型[] 数组名(推荐,区分类型与数组)
double[] scoreArr;
// 兼容写法:类型 数组名[](C语言风格,不推荐)
double scoreArr[];
重要注意事项:仅声明数组不会创建对象、不分配内存、无长度属性,只有通过new关键字实例化后,JVM才会在堆内存分配连续空间。
4.2.2 数组三种初始化方式
1. 动态初始化(先开辟空间,后赋值)
手动指定数组长度,系统自动为元素分配默认初始值,后续手动赋值。适用于提前确定数据条数,但不确定具体数据的场景。
全新示例:存储学生成绩数组
public class ArrayDynamicInit {
public static void main(String[] args) {
// 动态初始化:创建长度为5的浮点型数组
double[] studentScores = new double[5];
// 循环为数组元素赋值
studentScores[0] = 88.5;
studentScores[1] = 92.0;
studentScores[2] = 79.5;
studentScores[3] = 95.0;
studentScores[4] = 85.0;
// 遍历输出成绩
for (int i = 0; i < studentScores.length; i++) {
System.out.println("第" + (i + 1) + "名学生成绩:" + studentScores[i]);
}
}
}
2. 静态初始化(创建即赋值)
创建数组时直接传入所有元素值,系统自动根据元素个数确定数组长度,适用于数据已知、固定不变的场景。
全新示例:存储四季名称、固定数字序列
public class ArrayStaticInit {
public static void main(String[] args) {
// 字符串数组静态初始化
String[] seasons = {"春季", "夏季", "秋季", "冬季"};
// 整型数组静态初始化
int[] nums = {10, 20, 30, 40, 50};
System.out.println("四季数组长度:" + seasons.length);
System.out.println("数字数组长度:" + nums.length);
}
}
3. 默认初始化(系统自动赋值)
数组开辟空间后,未手动赋值时,系统会根据元素类型自动赋予默认值,是数组对象的固有特性。
默认值规则:
基本类型:整数默认0、浮点默认0.0、布尔默认false、字符默认空字符;
引用类型:所有对象、字符串默认null。
全新示例:查看各类数组默认初始值
public class ArrayDefaultInit {
public static void main(String[] args) {
int[] intArr = new int[3];
boolean[] boolArr = new boolean[3];
String[] strArr = new String[3];
System.out.println("整型数组默认值:" + intArr[0]);
System.out.println("布尔数组默认值:" + boolArr[0]);
System.out.println("字符串数组默认值:" + strArr[0]);
}
}
4.2.3 引用类型数组初始化(全新案例)
数组可以存储自定义对象,实现批量对象数据存储,是后续表格数据存储的基础。
class Book {
private String bookName;
private double price;
// 构造方法
public Book(String bookName, double price) {
this.bookName = bookName;
this.price = price;
}
// 重写toString
@Override
public String toString() {
return "书籍名称:" + bookName + ",价格:" + price + "元";
}
}
public class ReferenceArrayTest {
public static void main(String[] args) {
// 声明并创建长度为3的书籍对象数组
Book[] bookArr = new Book[3];
// 为数组元素赋值(存储对象)
bookArr[0] = new Book("Java编程入门", 59.9);
bookArr[1] = new Book("数据结构详解", 69.9);
bookArr[2] = new Book("算法实战指南", 79.9);
// 遍历对象数组
for (Book book : bookArr) {
System.out.println(book);
}
}
}
4.3 数组核心常用操作
4.3.1 普通for循环遍历数组
通过索引遍历,支持读取、修改数组元素,灵活性最高,开发中可精准操作指定下标元素。
全新示例:遍历数组并统计元素总和、平均值
public class ArrayForTraverse {
public static void main(String[] args) {
int[] data = {15, 28, 36, 42, 55, 67};
int sum = 0;
// 普通for循环遍历
for (int i = 0; i < data.length; i++) {
sum += data[i];
System.out.println("索引" + i + ",元素值:" + data[i]);
}
System.out.println("数组元素总和:" + sum);
System.out.println("数组元素平均值:" + (double) sum / data.length);
}
}
4.3.2 for-each增强循环遍历
专门用于快速遍历数组和集合,语法简洁、无需操作索引,仅可读取元素,无法修改数组内容。
全新示例:遍历存储城市名称的数组
public class ArrayForeachTraverse {
public static void main(String[] args) {
String[] citys = {"北京", "上海", "广州", "深圳", "杭州"};
// 增强for循环遍历
for (String city : citys) {
System.out.println("热门城市:" + city);
// 无法修改原数组元素,仅做读取操作
}
}
}
开发规范总结:需要下标、修改元素用普通for循环;仅快速遍历读取用for-each循环。
4.3.3 数组拷贝(System.arraycopy)
系统原生拷贝方法,效率极高,支持局部拷贝、全量拷贝、跨数组赋值,是集合扩容、数据迁移的底层核心方法。
方法参数说明:System.arraycopy(原数组, 原数组起始下标, 目标数组, 目标数组起始下标, 拷贝元素个数)
全新示例:商品数据数组拷贝
public class ArrayCopyTest {
public static void main(String[] args) {
// 原数组:商品列表
String[] goods = {"手机", "电脑", "平板", "耳机", "手表"};
// 目标数组:长度更大,预留扩容空间
String[] goodsBak = new String[8];
// 拷贝原数组全部数据到新数组
System.arraycopy(goods, 0, goodsBak, 0, goods.length);
// 遍历新数组
System.out.println("拷贝后的商品数组:");
for (String good : goodsBak) {
System.out.print(good + " ");
}
}
}
4.4 java.util.Arrays 工具类精讲
Arrays是Java官方提供的数组工具类,包含数组打印、排序、查找、填充等静态方法,无需手动造轮子,是开发高频工具。
4.4.1 数组格式化打印(toString)
直接打印数组变量会输出内存地址,Arrays.toString()可直接输出数组所有元素,简洁高效。
import java.util.Arrays;
public class ArraysPrintTest {
public static void main(String[] args) {
int[] numArr = {12, 45, 7, 89, 23};
// 直接打印:输出内存地址
System.out.println("数组内存地址:" + numArr);
// 工具类打印:输出元素内容
System.out.println("数组元素内容:" + Arrays.toString(numArr));
}
}
4.4.2 数组排序(sort)
支持基本类型、对象数组排序,默认升序排序,底层优化算法,效率高于手写基础排序。
import java.util.Arrays;
public class ArraysSortTest {
public static void main(String[] args) {
int[] randomArr = {56, 12, 98, 3, 77, 29};
System.out.println("排序前:" + Arrays.toString(randomArr));
// 数组升序排序
Arrays.sort(randomArr);
System.out.println("排序后:" + Arrays.toString(randomArr));
}
}
4.4.3 二分查找(binarySearch)
高效查找元素位置,必须先排序再查找,找到返回元素索引,未找到返回负数。
import java.util.Arrays;
public class ArraysSearchTest {
public static void main(String[] args) {
int[] scoreArr = {85, 92, 78, 96, 88, 90};
Arrays.sort(scoreArr);
System.out.println("排序后数组:" + Arrays.toString(scoreArr));
// 查找目标元素索引
int index = Arrays.binarySearch(scoreArr, 88);
System.out.println("元素88的索引位置:" + index);
}
}
4.4.4 数组局部填充(fill)
支持批量替换数组指定区间的元素,快速初始化、重置数组数据。
import java.util.Arrays;
public class ArraysFillTest {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6};
System.out.println("填充前:" + Arrays.toString(arr));
// 将索引1~3的元素替换为99
Arrays.fill(arr, 1, 4, 99);
System.out.println("填充后:" + Arrays.toString(arr));
}
}
4.5 多维数组(二维为主)
4.5.1 多维数组概念与内存结构
多维数组本质是存储数组的数组,开发中仅二维数组常用,用于存储表格、矩阵等二维数据,三维及以上几乎不用。
内存结构:二维数组外层存储多个一维数组的引用,每个一维数组可独立设置长度,支持不规则二维数组。
4.5.2 二维数组初始化与遍历
全新不规则二维数组案例
import java.util.Arrays;
public class TwoArrayTest {
public static void main(String[] args) {
// 动态初始化二维数组
int[][] twoArr = new int[3][];
twoArr[0] = new int[]{11, 22};
twoArr[1] = new int[]{33, 44, 55};
twoArr[2] = new int[]{66, 77, 88, 99};
// 双层循环遍历二维数组
for (int i = 0; i < twoArr.length; i++) {
System.out.println("第" + (i + 1) + "行数据:" + Arrays.toString(twoArr[i]));
}
}
}
4.5.3 二维数组存储表格数据
用二维数组模拟员工信息表格,每一行对应一条员工数据,实现结构化数据存储。
import java.util.Arrays;
public class ArrayTableTest {
public static void main(String[] args) {
// 二维数组存储员工表格:编号、姓名、年龄、岗位
Object[][] empTable = {
{1001, "张三", 22, "开发工程师"},
{1002, "李四", 24, "测试工程师"},
{1003, "王五", 23, "运维工程师"}
};
// 遍历表格数据
for (Object[] row : empTable) {
System.out.println(Arrays.toString(row));
}
}
}
4.6 JavaBean+数组存储结构化表格(开发规范)
二维数组存储表格存在数据类型混乱、可读性差的问题,企业开发统一使用 JavaBean实体类+数组存储表格数据,结构清晰、易于维护。
全新完整案例:员工实体+数组存储数据
// 员工实体类(JavaBean)
class Employee {
// 私有成员变量
private int empId;
private String empName;
private int age;
private String job;
// 无参、有参构造
public Employee() {}
public Employee(int empId, String empName, int age, String job) {
this.empId = empId;
this.empName = empName;
this.age = age;
this.job = job;
}
// 重写toString方法,方便打印数据
@Override
public String toString() {
return "员工编号:" + empId + ",姓名:" + empName + ",年龄:" + age + ",岗位:" + job;
}
// getter/setter方法
public int getEmpId() { return empId; }
public void setEmpId(int empId) { this.empId = empId; }
public String getEmpName() { return empName; }
public void setEmpName(String empName) { this.empName = empName; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public String getJob() { return job; }
public void setJob(String job) { this.job = job; }
}
// 测试类:数组存储实体对象
public class JavaBeanArrayTest {
public static void main(String[] args) {
// 实体类数组存储多条员工数据
Employee[] empArr = {
new Employee(1001, "赵六", 25, "后端开发"),
new Employee(1002, "孙七", 21, "前端开发"),
new Employee(1003, "周八", 26, "产品经理")
};
// 遍历打印结构化数据
for (Employee emp : empArr) {
System.out.println(emp);
}
}
}
4.6.1 Comparable接口实现对象数组排序
普通数组可直接排序,自定义对象数组需要实现Comparable接口,定义对象比较规则,实现排序功能。
全新案例:根据员工年龄排序
import java.util.Arrays;
// 实现Comparable接口,重写比较规则
class Staff implements Comparable<Staff> {
private String name;
private int age;
public Staff(String name, int age) {
this.name = name;
this.age = age;
}
// 重写比较方法:按年龄升序排序
@Override
public int compareTo(Staff o) {
return this.age - o.age;
}
@Override
public String toString() {
return "姓名:" + name + ",年龄:" + age;
}
}
public class ObjectArraySortTest {
public static void main(String[] args) {
Staff[] staffArr = {
new Staff("小明", 28),
new Staff("小红", 22),
new Staff("小刚", 25)
};
Arrays.sort(staffArr);
System.out.println("按年龄升序排序结果:");
for (Staff staff : staffArr) {
System.out.println(staff);
}
}
}
4.7 数组经典算法(全新原创代码)
算法可视化练习工具:https://visualgo.net/,可直观观察排序、查找的执行过程,快速理解算法原理。
4.7.1 基础冒泡排序
核心原理:相邻元素两两对比,逆序则交换,每轮排序将当前最大值冒泡到末尾,多轮循环完成整体排序。
import java.util.Arrays;
public class BaseBubbleSort {
public static void main(String[] args) {
// 随机无序数组
int[] arr = {22, 5, 88, 12, 67, 3, 49};
System.out.println("排序前数组:" + Arrays.toString(arr));
bubbleSort(arr);
System.out.println("排序后数组:" + Arrays.toString(arr));
}
// 基础冒泡排序算法
public static void bubbleSort(int[] arr) {
int temp;
// 外层控制排序轮数
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]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
4.7.2 优化冒泡排序
优化原理:添加有序标记,若某一轮未发生任何元素交换,说明数组已有序,直接终止循环,减少无效运算。
import java.util.Arrays;
public class OptimizeBubbleSort {
public static void main(String[] args) {
int[] arr = {13, 6, 79, 25, 33, 8, 56};
System.out.println("排序前数组:" + Arrays.toString(arr));
optimizeSort(arr);
System.out.println("排序后数组:" + Arrays.toString(arr));
}
public static void optimizeSort(int[] arr) {
int temp;
for (int i = 0; i < arr.length - 1; i++) {
// 标记本轮是否有序
boolean isSorted = true;
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
isSorted = false;
}
}
// 数组已有序,直接退出循环
if (isSorted) {
break;
}
}
}
}
4.7.3 二分查找(折半查找)
核心原理:基于有序数组,每次取中间元素对比,缩小一半查找区间,查询效率远高于遍历查找。
import java.util.Arrays;
public class BinarySearchTest {
public static void main(String[] args) {
int[] numArr = {9, 23, 15, 88, 41, 66, 37};
int target = 41;
// 二分查找前必须排序
Arrays.sort(numArr);
System.out.println("有序数组:" + Arrays.toString(numArr));
int index = binarySearch(numArr, target);
System.out.println("目标元素" + target + "的索引位置:" + (index == -1 ? "未找到" : index));
}
// 手写二分查找算法
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
// 计算中间索引
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] > target) {
// 目标更小,向左区间查找
right = mid - 1;
} else {
// 目标更大,向右区间查找
left = mid + 1;
}
}
// 未找到返回-1
return -1;
}
}
本章核心知识点总结
-
数组是固定长度、同类型的有序存储集合,属于引用类型,内存空间固定不可变;
-
数组分为动态、静态、默认三种初始化方式,适配不同业务场景;
-
普通for循环可修改元素,for-each仅能遍历读取,System.arraycopy实现高效数组拷贝;
-
Arrays工具类封装排序、查找、填充、打印等常用方法,是开发必备工具;
-
二维数组存储表格数据,JavaBean+数组是企业结构化数据存储的标准写法;
-
自定义对象通过Comparable接口定义排序规则,实现对象数组排序;
-
优化冒泡排序减少无效循环,二分查找基于有序数组实现高效检索,是入门核心算法。
更多推荐

所有评论(0)