1. 简介
数组(Array)是一种线性数据结构,用于存储多个元素,这些元素在内存中是连续存储的。数组的每个元素都可以通过索引访问,通常索引是从0开始的。
数组的特点:
固定大小:一旦创建,数组的大小就固定了,无法动态增加或减少元素数量。你需要在创建时确定数组的长度。
存储类型一致:数组中的所有元素必须是同一数据类型。
高效的随机访问:可以通过索引直接访问数组中的元素,时间复杂度为O(1)。
内存连续:数组的元素在内存中是连续存储的,这使得它非常适合于需要频繁随机访问的场景。
2. 分类
2.1 一维数组
一维数组是最常用的数组,其他很多数据结构的变种也都是从一维数组来的。例如 HashMap 的拉链寻址结构,ThreadLocal 的开放寻址结构,都是从一维数组上实现的。
HashMap结构:
ThreadLocal结构:数组大小根据元素个数确定(比元素个数大)
2.2 二维数组
二维数组是数组的数组。它可以看作是一个矩阵,具有行和列的结构。
arr[0][0] = 5,arr[1][0] = 78。二维数组在日常开发中不太常见,在一些算法逻辑与数学计算中倒是可以使用。
3. 实例
数组的基本操作:
-
访问元素:通过索引访问数组元素,如
arr[i]
。 -
插入元素:插入元素通常需要移动数组中部分元素来腾出空间,这种操作的时间复杂度是O(n)。
-
删除元素:删除元素时,通常需要移动数组中的其他元素来填补空缺,时间复杂度也是O(n)。
-
更新元素:直接通过索引更新数组中的元素,时间复杂度为O(1)。
基于数组的基本操作实现一个简单的类ArrayList:
import java.util.Arrays;
public class SimpleArrayList {
private Object[] arr; // 存储元素的数组
private int size; // 当前数组中存储的元素个数
// 默认容量
private static final int DEFAULT_CAPACITY = 10;
// 构造函数
public SimpleArrayList() {
arr = new Object[DEFAULT_CAPACITY];
size = 0;
}
// 添加元素
public void add(Object element) {
// 如果数组满了,扩容
if (size == arr.length) {
resize();
}
arr[size++] = element;
}
// 删除指定索引的元素
public void remove(int index) {
if (index < 0 || index >= size) {
System.out.println("索引越界!");
return;
}
// 删除元素后,将后面的元素前移
for (int i = index; i < size - 1; i++) {
arr[i] = arr[i + 1];
}
arr[--size] = null; // 更新 size,并将最后一个元素置为 null
}
// 获取指定索引的元素
public Object get(int index) {
if (index < 0 || index >= size) {
System.out.println("索引越界!");
return null;
}
return arr[index];
}
// 查找元素的索引
public int indexOf(Object element) {
for (int i = 0; i < size; i++) {
if (arr[i].equals(element)) {
return i;
}
}
return -1; // 未找到元素
}
// 扩容数组
private void resize() {
int newCapacity = arr.length * 2;
arr = Arrays.copyOf(arr, newCapacity);
}
// 打印数组元素
public void printList() {
System.out.print("ArrayList: ");
for (int i = 0; i < size; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
// 获取当前元素个数
public int size() {
return size;
}
}
该实例实现了如下功能:
添加元素 (add()
)
获取元素 (get()
)
删除元素 (remove()
)
查找元素的索引 (indexOf()
)
扩容(当数组满时,自动扩展数组)
打印当前数组 (printList()
)
测试:
public class ArrayListTest{
public static void main(String[] args) {
SimpleArrayList list = new SimpleArrayList();
// 添加元素
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add("Hello");
// 打印数组
list.printList(); // 输出: ArrayList: 10 20 30 40 Hello
// 获取指定索引的元素
System.out.println("索引2的元素: " + list.get(2)); // 输出: 30
// 查找元素的索引
System.out.println("元素30的索引: " + list.indexOf(30)); // 输出: 2
System.out.println("元素100的索引: " + list.indexOf(100)); // 输出: -1
// 删除元素
list.remove(2);
list.printList(); // 输出: ArrayList: 10 20 40 Hello
// 删除不存在的索引
list.remove(10); // 输出: 索引越界!
// 打印当前大小
System.out.println("当前数组大小: " + list.size()); // 输出: 4
}
}
这个实例可以做进一步的优化和拓展:
动态缩小数组:目前只是增加了容量,没有减少容量。可以添加一个“收缩”功能,比如当数组的使用量低于一定比例时,减少数组的大小。
线程安全:如果希望这个实现是线程安全的,可以考虑使用 synchronized
关键字,或者使用 java.util.concurrent
中的线程安全集合类。
泛型支持:当前实现使用了 Object[]
来存储所有元素,如果想要支持更严格的类型检查,可以使用泛型类来代替 Object
。
4. 二维数组拓展
一个简单的二维数组相加示例,打开新世界大门。
public class MatrixAddition {
public static void main(String[] args) {
int[][] matrix1 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int[][] matrix2 = {
{9, 8, 7},
{6, 5, 4},
{3, 2, 1}
};
int rows = matrix1.length; // 行数
int columns = matrix1[0].length; // 列数
int[][] result = new int[rows][columns];
// 矩阵相加
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
result[i][j] = matrix1[i][j] + matrix2[i][j];
}
}
// 打印结果矩阵
System.out.println("矩阵相加的结果:");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
System.out.print(result[i][j] + " ");
}
System.out.println();
}
}
}
二维数组 是数组的数组,适合存储和处理矩阵或表格数据。
创建方式 可以是静态初始化(定义时就指定所有值)或者动态初始化(先声明大小再赋值)。
遍历二维数组 通常使用嵌套 for
循环来处理每个元素。
可以使用 不规则数组(即锯齿状数组)来处理列数不等的情况。
不积跬步,无以至千里 --- xiaokai