深入理解数组:概念、用法和常用技巧

不管现实多么惨不忍睹,都要持之以恒地相信,这只是黎明前短暂的黑暗而已。不要惶恐眼前的难关迈不过去,不要担心此刻的付出没有回报,别再花时间等待天降好运。真诚做人,努力做事!你想要的,岁月都会给你。深入理解数组:概念、用法和常用技巧,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

深入理解数组

引言

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

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

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

数组的基本概念

什么是数组?

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

数组的特点

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

数组的声明和初始化⭐️

// 声明数组
int[] intArray;

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

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

数组的常见操作

1、访问和修改元素

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

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

2、遍历数组

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

// 使用增强for循环
for (int num : intArray) {
    System.out.println(num);
}

3、多维数组的创建

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

// 访问和修改元素
matrix[1][2] = 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);

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);

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));

7、数组的工具类

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

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

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

// 二分查找 -- binarySearch
int index = Arrays.binarySearch(intArray, 3); 

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));

应用场景

数据库查询结果

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

文件处理

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

缓存

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

排序与搜索

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

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

越界问题

**避免数组越界:**在访问数组元素时,确保索引值在数组的有效范围内,以防止数组越界异常。访问不存在的下标会导致 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;
    }
}

数组拷贝

使用 System.arraycopyArrays.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)

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

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

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

内存布局示意图:

1 2 3 4 5 6 7 8 9

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

列主序布局(Column-major order)

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

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

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

内存布局示意图:

1 4 7 2 5 8 3 6 9

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

总结

  • 具体的内存布局方式会受到编译器、操作系统和硬件架构的影响。
  • 在大多数情况下,行主序布局是默认的,并且在常见的编程语言中使用得更广泛。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/189745.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!