当前位置:   article > 正文

Java数组详解_获取二维数组的行数和列数

获取二维数组的行数和列数

引言

数组是Java中非常重要的数据结构之一,它允许我们存储相同类型的数据元素的集合,并可以通过索引来访问这些元素。本文将详细介绍Java数组的基础定义、使用方法、底层存储原理,以及与其他数据类型的转换,帮助读者深入理解和应用Java数组。

一、数组的基础定义

数组是一种引用数据类型,用于存储相同类型的元素的集合。在Java中,数组可以通过以下方式定义:

  1. dataType[] arrayName; // 推荐方式
  2. // 或者
  3. dataType arrayName[]; // 早期Java版本中常见,但现在已不推荐

其中dataType表示数组元素的类型,arrayName是数组变量的名称。数组定义后,需要进行初始化才能使用。

二、数组的使用

2.1. 数组的初始化

数组可以通过静态初始化和动态初始化两种方式来进行初始化。

  1. // 静态初始化
  2. int[] numbers = {12345};
  3. // 动态初始化
  4. int[] anotherNumbers = new int[5];
  5. anotherNumbers[0] = 10;
  6. anotherNumbers[1] = 20;
  7. // ... 其他元素赋值

2.2. 访问数组元素

通过索引可以访问数组中的元素,索引从0开始。

int firstElement = numbers[0]; // 访问第一个元素

2.3. 数组长度

通过数组的length属性可以获取数组的长度(元素个数)。

int length = numbers.length;

4. 数组相关方法

Java的java.util.Arrays类提供了许多操作数组的有用方法,如排序、搜索、填充等。

  1. import java.util.Arrays;
  2. // 排序数组
  3. Arrays.sort(numbers);
  4. // 打印数组内容
  5. System.out.println(Arrays.toString(numbers));

三、数组的底层存储原理

在Java中,数组在内存中是连续存储的。数组名实际上是一个引用,它指向数组在内存中的首地址。通过索引访问数组元素时,Java会根据首地址和索引计算出具体元素的内存地址,然后进行操作。这种连续存储的方式使得数组在访问元素时具有较高的效率。

3.1. 内存分配:

当我们声明一个数组时,Java会根据数组的类型和长度在堆内存中分配一块连续的内存空间。 一组连续内存空间,在这里插入图片描述

3.2.内存布局

数组按照索引顺序存储元素,并且每个元素的大小是固定的。Java中的数组是通过偏移量来访问元素的。偏移量是相对于数组头部的固定偏移量,通过偏移量可以计算出元素在内存中的位置。

3.3. 访问元素

通过索引可以直接访问数组中的元素。Java虚拟机会根据索引和偏移量计算出元素的内存地址,然后直接访问该地址来获取或修改元素的值。

注意

Java的数组是固定长度的,一旦分配了内存空间,其长度在运行时是不可改变的。如果需要动态改变数组大小,需要使用其他数据结构,如ArrayList。

四、数组与其他数据类型的转换

4.1. 数组与List的转换

Java中的List(如ArrayList)是另一种常用的集合类型,它提供了动态数组的功能。Java 5及以上版本提供了方便的转换方法。

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. // 数组转为List
  4. int[] array = {12345};
  5. List<Integer> list = new ArrayList<>(Arrays.asList(array));
  6. // List转为数组
  7. Integer[] listArray = list.toArray(new Integer[0]);
  8. // 或者转换为原始类型数组
  9. int[] primitiveArray = list.stream().mapToInt(Integer::intValue).toArray();
  • 注意: 数组的长度是固定的,而集合框架的长度是可变的。在进行数组到集合的转换时,数组的长度不会改变;而在进行集合到数组的转换时,需要事先确定目标数组的长度。

4.2. 数组与集合框架的其他类型的转换

除了List,Java集合框架还提供了Set、Queue等其他类型的集合。虽然它们没有直接的转换方法,但可以通过遍历数组或使用Stream API来实现转换。

五、其他

5.1. 多维数组

Java还支持多维数组,如二维数组、三维数组等。多维数组可以看作是数组的数组,用于存储更复杂的数据结构。

int[][] matrix = new int[2][3]; // 定义一个2x3的二维数组

5.1.1.二维数组

二维数组本质上是一维数组的数组,即每个元素都是一个一维数组。二维数组常用于表示表格或矩阵数据。

5.1.1.1. 定义与初始化

二维数组可以通过静态初始化和动态初始化两种方式进行。

  1. // 静态初始化  
  2. int[][] staticArray = {{123}, {456}, {789}};  
  3.   
  4. // 动态初始化  
  5. int[][] dynamicArray = new int[3][]; // 只指定了第一维的长度  
  6. dynamicArray[0] = new int[2];  
  7. dynamicArray[1] = new int[3];  
  8. dynamicArray[2] = new int[4];  
  9. // 为每个一维数组的元素赋值
5.1.1.2.访问元素

通过两个索引可以访问二维数组中的元素,第一个索引表示行,第二个索引表示列。

int element = staticArray[0][1]; // 访问第一行第二列的元素
5.1.1.3.使用场景

二维数组常用于表示具有行和列结构的数据,如矩阵运算、图像处理等。

5.1.2.三维数组

三维数组是数组的数组的数组,即每个元素都是一个二维数组。三维数组常用于表示具有三个维度的数据结构。

5.1.2.1. 定义与初始化

三维数组同样可以通过静态初始化和动态初始化进行。

  1. // 静态初始化  
  2. int[][][] static3DArray = {  
  3.     {{123}, {456}},  
  4.     {{789}, {101112}}  
  5. };  
  6.   
  7. // 动态初始化  
  8. int[][][] dynamic3DArray = new int[2][2][3];  
5.1.2.2.访问元素

通过三个索引可以访问三维数组中的元素,分别表示第一维、第二维和第三维的位置。

int element = static3DArray[0][1][2]; // 访问第一维第0个,第二维第1个,第三维第2个元素
5.1.2.3.使用场景

三维数组在科学计算、图像处理、空间数据处理等领域有着广泛的应用,用于表示具有三个维度特征的数据结构。

5.2. 数组的复制

Java中数组的复制需要注意浅拷贝和深拷贝的区别

  • 浅拷贝: 拷贝之后,通过新数组修改值,如果原数组的值也被改变, 就是浅拷贝

  • 深拷贝: 拷贝之后,通过新数组修改值,如果原数组的值没有被改变,就是深拷贝

5.2.1 浅拷贝

使用Arrays.copyOf()或数组赋值操作是浅拷贝,只复制了引用而不是实际对象。

5.2.2 深拷贝

5.2.2.1.循环遍历方式

遍历原数组,逐个复制元素到新数组中。对于引用类型的元素,还需要对每个元素执行深拷贝操作。

  • 简易版(以String []为例)

  1. public static String[] deepCopyArray(String[] source) {
  2.     if (source == null) {
  3.         return null;
  4.     }
  5.     
  6.     String[] destination = new String[source.length];
  7.     for (int i = 0; i < source.length; i++) {
  8.         destination[i] = new String(source[i]);
  9.     }
  10.     
  11.     return destination;
  12. }
  • 通用方法:

  1. public static <T> T[] deepCopyArray(T[] source) {
  2.     if (source == null) {
  3.         return null;
  4.     }
  5.     
  6.     // 创建一个新的数组作为目标数组,并拷贝源数组的内容
  7.     T[] destination = Arrays.copyOf(source, source.length);
  8.     
  9.     // 遍历源数组,对引用类型的元素执行深拷贝操作
  10.     for (int i = 0; i < source.length; i++) {
  11.         if (source[i] instanceof Cloneable) {
  12.             try {
  13.                 // 获取元素的类对象
  14.                 Class<?> clazz = source[i].getClass();
  15.                 // 获取元素的clone()方法
  16.                 Method cloneMethod = clazz.getMethod("clone");
  17.                 // 调用clone()方法得到元素的副本,并将其赋值给目标数组
  18.                 destination[i] = (T) cloneMethod.invoke(source[i]);
  19.             } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
  20.                 e.printStackTrace();
  21.             }
  22.         }
  23.     }
  24.     
  25.     return destination;
  26. }
  • 注意: 要实现深拷贝,源数组中的引用类型元素需要实现Cloneable接口并重写clone()方法。否则,在获取clone()方法时可能会抛出NoSuchMethodException异常。另外,需要注意捕获和处理可能抛出的异常,以确保代码的健壮性。

5.2.2.1.方式二:序列化方式

将原数组进行序列化,然后再反序列化成一个新数组

  1. public static <T> T[] deepCopyArray(T[] source) {
  2.     if (source == null) {
  3.         return null;
  4.     }
  5.     
  6.     try {
  7.         // 创建一个字节数组输出流
  8.         ByteArrayOutputStream bos = new ByteArrayOutputStream();
  9.         // 创建一个对象输出流,并将其连接到字节数组输出流
  10.         ObjectOutputStream oos = new ObjectOutputStream(bos);
  11.         // 将原数组写入对象输出流,实现序列化
  12.         oos.writeObject(source);
  13.         oos.flush();
  14.         oos.close();
  15.         
  16.         // 创建一个字节数组输入流,并用字节数组输出流的数据来初始化
  17.         ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
  18.         // 从对象输入流读取对象数据,实现反序列化
  19.         T[] destination = (T[]) ois.readObject();
  20.         ois.close();
  21.         
  22.         return destination;
  23.     } catch (IOException | ClassNotFoundException e) {
  24.         e.printStackTrace();
  25.     }
  26.     
  27.     return null;
  28. }

说明: 需要确保数组中的对象类型是可序列化的,否则在执行序列化的过程中可能会抛出异常。另外,在使用该方法时,需要考虑序列化和反序列化的性能消耗以及所引起的额外开销。

六、总结

数组是Java编程中非常基础且重要的数据结构,它提供了存储和操作同类型数据元素的有效方式。通过本文的介绍,读者应该对Java数组的定义、使用、底层存储原理以及与其他数据类型的转换有了更深入的了解。在实际编程中,应根据具体需求选择合适的数据结构,并灵活运用数组及其相关方法。

By the way

推荐数据结构可视化的学习网站 Data Structure Visualizations

学习界面如下图,对于学习数据结构底层原理很有用

  • 首页

  • 栈(数组实现)

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号