沉梦听雨的编程指南 沉梦听雨的编程指南
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • RPC
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 多媒体框架
  • 虚拟机
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)

沉梦听雨

时间是最好的浸渍剂,而沉淀是最好的提纯器🚀
首页
  • 基础篇
  • 集合篇
  • 并发篇
  • JVM
  • 新特性
  • 计算机网络
  • 操作系统
  • 数据结构与算法
  • 基础篇
  • MySql
  • Redis
  • 达梦数据库
  • Spring
  • SpringBoot
  • Mybatis
  • Shiro
  • 设计须知
  • UML画图
  • 权限校验
  • 设计模式
  • API网关
  • RPC
  • 消息队列
  • SpringCloud
  • 分布式事务
  • 云存储
  • 搜索引擎
  • 多媒体框架
  • 虚拟机
  • 开发工具篇
  • 工具库篇
  • 开发技巧篇
  • 工具类系列
  • 随笔
  • 前端环境搭建
  • HTML与CSS
  • JS学习
  • Vue3入门
  • Vue3进阶
  • 黑马Vue3
  • 脚手架搭建
  • 瑞吉外卖
  • 黑马点评
  • vue-blog
  • 沉梦接口开放平台
  • 用户中心
  • 聚合搜索平台
  • 仿12306项目
  • 壁纸小程序项目
  • RuoYi-Vue
  • 博客搭建
  • 网站收藏箱
  • 断墨寻径摘录
  • 费曼学习法
Github (opens new window)
  • 开发工具篇

  • 工具库篇

  • 开发技巧篇

  • 工具类系列

  • 随笔

    • HttpServletRequest知识小结
    • Spring MVC 项目构建流程
    • 虚拟机固定ip地址
    • 项目部署
    • 深入理解数组
      • 引言
      • 数组的基本概念
        • 什么是数组?
        • 数组的特点
        • 数组的声明和初始化⭐️
      • 数组的常见操作
        • 1、访问和修改元素
        • 2、遍历数组
        • 3、多维数组的创建
        • 4、查找最大/最小值
        • 5、数组过滤/筛选
        • 6、数组转换
        • 7、数组的工具类
        • 8、数组合并
      • 应用场景
        • 数据库查询结果
        • 文件处理
        • 缓存
        • 排序与搜索
      • 数组的常见问题与技巧⭐️
        • 越界问题
        • 动态扩展
        • 空指针异常处理
        • 数组拷贝
        • 数组与集合的转换
      • 性能问题
      • 多维数组的内存布局
        • 行主序布局(Row-major order)
        • 列主序布局(Column-major order)
        • 总结
    • IIS使用小记
    • From的两种类型解析
    • 开发疑惑
    • 开发小记
    • bug小记
  • 开发日常
  • 随笔
沉梦听雨
2023-11-17
目录

深入理解数组

# 深入理解数组

# 引言

数组是计算机科学中最基础、最常用的数据结构之一。在 Java 中,数组提供了一种有序、连续的存储方式,它不仅在内存中占据一块连续的空间,还支

持通过索引快速访问和操作元素。本文将深入探讨 Java 中数组的基本概念、常见用法和后端开发中的实际应用。

最近写代码时发现自己对于数组的知识掌握得不是很好,所以特地梳理了这么一篇文章,希望对您有所帮助。

# 数组的基本概念

# 什么是数组?

数组是一种线性数据结构,由相同类型的元素构成,这些元素在内存中按照一定的顺序依次存储。每个元素可以通过数组的下标(索引)来访问,而下标从零开始。

# 数组的特点

  • **相同类型:**数组中的元素必须是相同的数据类型,可以是基本数据类型或对象。
  • **连续存储:**数组的元素在内存中占据一块连续的空间,这使得通过索引快速访问成为可能。
  • **固定长度:**数组的长度是固定的,一旦创建就无法改变。

# 数组的声明和初始化⭐️

// 声明数组
int[] intArray;

// 初始化数组 -- 初始化长度为5的整型数组
intArray = new int[5];

// 同时声明和初始化
int[] intArray = new int[]{1, 2, 3, 4, 5};
1
2
3
4
5
6
7
8

# 数组的常见操作

# 1、访问和修改元素

// 获取下标为2的元素
int value = intArray[2]; 

// 修改下标为2的元素的值为10
intArray[2] = 10;
1
2
3
4
5

# 2、遍历数组

// 使用普通for循环
for (int i = 0; i < intArray.length; i++) {
    System.out.println(intArray[i]);
}

// 使用增强for循环
for (int num : intArray) {
    System.out.println(num);
}
1
2
3
4
5
6
7
8
9

# 3、多维数组的创建

// 3行4列的二维数组
int[][] matrix = new int[3][4]; 

// 访问和修改元素
matrix[1][2] = 5;
1
2
3
4
5

# 4、查找最大/最小值

int[] numbers = {1, 3, 5, 2, 4};
int max = numbers[0];
int min = numbers[0];

for (int i = 1; i < numbers.length; i++) {
    if (numbers[i] > max) {
        max = numbers[i]; // 更新最大值
    }
    if (numbers[i] < min) {
        min = numbers[i]; // 更新最小值
    }
}

System.out.println("最大值:" + max);
System.out.println("最小值:" + min);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5、数组过滤/筛选

int[] numbers = {1, 2, 3, 4, 5};
List<Integer> evenNumbers = new ArrayList<>();

for (int number : numbers) {
    if (number % 2 == 0) {
        evenNumbers.add(number); // 将偶数筛选出来并添加到新的列表中
    }
}

System.out.println(evenNumbers);
1
2
3
4
5
6
7
8
9
10

# 6、数组转换

int[] numbers = {1, 2, 3, 4, 5};
String[] strings = new String[numbers.length];

for (int i = 0; i < numbers.length; i++) {
    strings[i] = String.valueOf(numbers[i]); // 将整数数组转换为字符串数组
}

System.out.println(Arrays.toString(strings));
1
2
3
4
5
6
7
8

# 7、数组的工具类

Java 提供了 Arrays 类,其中包含许多有用的数组操作方法:

// 复制数组 -- copyOf
int[] newArray = Arrays.copyOf(intArray, 10);

// 对数组进行排序 -- sort
Arrays.sort(intArray); 

// 二分查找 -- binarySearch
int index = Arrays.binarySearch(intArray, 3); 
1
2
3
4
5
6
7
8

# 8、数组合并

int[] numbers1 = {1, 2, 3};
int[] numbers2 = {4, 5, 6};

int[] mergedNumbers = new int[numbers1.length + numbers2.length];

// 复制numbers1到mergedNumbers
System.arraycopy(numbers1, 0, mergedNumbers, 0, numbers1.length); 
// 复制numbers2到mergedNumbers
System.arraycopy(numbers2, 0, mergedNumbers, numbers1.length, numbers2.length);

System.out.println(Arrays.toString(mergedNumbers));
1
2
3
4
5
6
7
8
9
10
11

# 应用场景

# 数据库查询结果

将数据库查询结果存储在数组中进行处理。

# 文件处理

存储文件信息或处理文件数据。

# 缓存

使用数组作为缓存结构,例如缓存最近的请求记录等。

# 排序与搜索

在需要有序数据时,数组的排序和搜索功能很常见。

# 数组的常见问题与技巧⭐️

# 越界问题

**避免数组越界:**在访问数组元素时,确保索引值在数组的有效范围内,以防止数组越界异常。访问不存在的下标会导致 ArrayIndexOutOfBoundsException 异常。

# 动态扩展

**使用动态数组或集合:**数组长度通常是固定的,如果需要动态扩展,考虑使用动态数组(ArrayList)等集合类。

# 空指针异常处理

**注意空指针异常:**在操作数组时,特别是在从其他方法或函数返回数组时,要注意空指针异常的处理。

以下是一个示例代码,展示了如何处理可能引发空指针异常的情况:

public class ArrayExample {
    public static void main(String[] args) {
        int[] numbers = getNumbers();
      
        // 判断是否为空
        if (numbers != null) {
            // 数组不为空,可以进行操作
            for (int number : numbers) {
                System.out.println(number);
            }
        } else {
            System.out.println("数组为空");
        }
    }

    public static int[] getNumbers() {
        // 模拟从其他方法或函数返回数组的情况
        int[] numbers = null; // 假设获取数组的过程出现问题,返回null
        return numbers;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 数组拷贝

使用 System.arraycopy 或 Arrays.copyOf 进行数组的复制。

# 数组与集合的转换

使用 Arrays.asList 将数组转换为 List,或使用 List.toArray 将 List 转换为数组。

# 性能问题

以下是一些与数组性能相关的问题:

  1. 访问时间:数组的访问时间是常数时间(O(1)),因为可以通过索引直接访问数组中的元素。这是数组的一个重要优势,因为它允许快速访问和修改元素。
  2. 内存占用:数组在内存中占用连续的一块空间。这种紧凑的存储方式可以提高访问效率,但也意味着数组的大小是固定的。如果需要动态调整数组的大小,可能需要重新分配内存并复制元素,这可能会导致性能损失。
  3. 插入和删除元素:在数组中插入或删除元素通常涉及到元素的移动操作。如果插入或删除的位置靠近数组的开头,需要移动的元素较多,可能会导致较大的性能开销。在这种情况下,使用链表等数据结构可能更有效。(不适合频繁插入删除的场景)
  4. 遍历和搜索:遍历数组的性能是线性时间(O(n)),其中 n 是数组的大小。这意味着随着数组的增长,遍历所需的时间也线性增加。对于大型数组,遍历和搜索可能会显著影响性能。
  5. 多维数组:多维数组的性能可以受到访问模式的影响。例如,对于二维数组,按行访问通常比按列访问更高效,因为二维数组的内存布局是按行存储的。了解数组的内存布局和访问模式可以帮助优化性能。

为了提高数组的性能,可以考虑以下几点:

  • 避免不必要的数组复制和内存重新分配。 -- 尽量在初始化时明确数组大小
  • 尽量使用原始数据类型(如int、double)而不是包装类(如Integer、Double),以减少对象创建和垃圾回收的开销。
  • 考虑使用其他数据结构,如链表、树或哈希表,根据具体需求选择更适合的数据结构。 -- 根据实际场景需求
  • 对于大型数组,可以采用分块、分段或分布式存储等技术来减少单个数组的大小,提高访问效率。
  • 如果需要频繁插入或删除元素,可以考虑使用其他数据结构,如链表或动态数组,以避免元素移动的开销。
  • 对于多维数组,了解内存布局并根据访问模式进行优化,可以提高访问性能。
  • 在遍历或搜索数组时,注意使用合适的算法和数据结构来优化性能,如二分查找、哈希表等。

# 多维数组的内存布局

笔者遇到的不多,简单了解一下

多维数组在内存中的布局方式取决于编程语言和具体实现。在大多数编程语言中,多维数组实际上是通过一维数组来表示的,使用某种索引计算方式来映射多维索引到一维数组的索引。

常见的多维数组内存布局方式有两种:行主序(Row-major order)和列主序(Column-major order)。这两种布局方式在访问多维数组的时候会有所不同。

# 行主序布局(Row-major order)

在行主序布局中,多维数组的内存存储按照行的顺序进行排列。也就是说,相邻的元素在内存中是连续存储的,而行之间是按照顺序排列的。这种布局方式在大多数编程语言中被广泛采用,包括 C、C++ 和 Java 等。

下面是一个二维数组的行主序内存布局示例:

// 二维数组
int[][] matrix1 = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
1
2
3
4
5
6

内存布局示意图:

1 2 3 4 5 6 7 8 9
1

通过行主序布局,数组中的元素在内存中是按照从左到右、从上到下的顺序排列的。

# 列主序布局(Column-major order)

在列主序布局中,多维数组的内存存储按照列的顺序进行排列。也就是说,相邻的元素在内存中是连续存储的,而列之间是按照顺序排列的。这种布局方式在一些科学计算领域中常见,例如 Fortran 语言。

下面是一个二维数组的列主序内存布局示例:

// 二维数组
int[][] matrix2 = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
1
2
3
4
5
6

内存布局示意图:

1 4 7 2 5 8 3 6 9
1

通过列主序布局,数组中的元素在内存中是按照从上到下、从左到右的顺序排列的。

# 总结

  • 具体的内存布局方式会受到编译器、操作系统和硬件架构的影响。
  • 在大多数情况下,行主序布局是默认的,并且在常见的编程语言中使用得更广泛。
上次更新: 2024/9/25 11:16:13
项目部署
IIS使用小记

← 项目部署 IIS使用小记→

Theme by Vdoing | Copyright © 2023-2025 沉梦听雨 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式