当前位置: 首页 > news >正文

Java 基础知识点——数组相关

目录

1️⃣基础知识点:

1. 数组的定义与基本特性

2. 数组的创建

3. 数组的访问与操作

4. 数组的特性与限制

5. 数组的常用方法

6. 多维数组

7. 数组的内存模型

8. 数组与集合的对比

9. 数组的克隆

10. 注意事项

✅总结:

2️⃣相关的面试问题:

1. Java 中数组是对象吗?数组的类型是什么?

2. 数组和 ArrayList 有什么区别?

3. 如何声明并初始化一个长度为 10 的 int 数组,所有值为 42?

4. 下面代码会不会抛出异常?

5. 如何实现数组的反转?时间复杂度是多少?

6. 对一个数组排序,你会选择哪种方式?为什么?

7. Java 数组在内存中是如何存储的?

8. 数组的 clone() 是浅拷贝还是深拷贝?

9. Arrays.asList() 有什么坑?

10. Java 中如何实现不定长数组?

11. 手写一个方法:找出数组中只出现一次的元素(其他元素出现两次)

12. 如何用数组实现一个栈/队列?

13. 实现一个 O(1) 的获取数组最大值结构(辅助栈思路)

📌 Part 1: 基于算法的数组面试题

1. 找出数组中只出现一次的数字(其余出现两次)

2. 数组中两个数的和等于目标值

3. 移动零

4. 最大子数组和

5. 数组旋转

6. 数组去重(原地去重)

7. 合并两个有序数组

8. 寻找数组的第 K 大元素

9. 二维数组查找(行列有序)

10. 二维数组旋转(原地)

📌 Part 2: 基于 JVM 原理的数组面试题(参考回答)

1. JVM 中数组如何分配内存?

2. 数组与对象在内存布局上的异同?

3. 为何数组越界是运行时异常?

4. 数组长度为何不可变?

5. JVM 优化数组访问的方式?

6. 为何数组可以存放基本类型而泛型不行?

7. 多维数组在内存中的表示?

8. Arrays.asList() 与原数组关系?

9. clone() 是浅拷贝还是深拷贝?

10. 使用 Unsafe 或反射操作数组?


具体内容如下:

1️⃣基础知识点:

1. 数组的定义与基本特性

  • 定义:数组是一个固定大小的、相同类型元素的集合。可以存储基本数据类型(如int、char等)或对象类型。
  • 大小固定:数组一旦创建,其大小不能改变。
  • 索引:数组的索引从0开始,最后一个元素的索引为length - 1

2. 数组的创建

  • 一维数组
    int[] array1 = new int[5]; // 创建一个长度为5的整型数组
    int[] array2 = {1, 2, 3, 4, 5}; // 直接初始化
    
  • 二维数组
    int[][] matrix = new int[3][4]; // 创建一个3行4列的二维数组
    int[][] matrix2 = { {1, 2}, {3, 4}, {5, 6} }; // 直接初始化
    

3. 数组的访问与操作

  • 访问元素:通过索引访问元素,例如array1[0]
  • 遍历数组
    • 使用for循环:
      for (int i = 0; i < array1.length; i++) {System.out.println(array1[i]);
      }
      
    • 使用增强for循环:
      for (int value : array1) {System.out.println(value);
      }
      

4. 数组的特性与限制

  • 类型一致性:数组中的所有元素必须是相同类型。
  • 内存分配:数组在内存中是连续分配的,效率较高,但大小固定的特性可能导致内存浪费。

5. 数组的常用方法

  • 数组的复制
    • 使用System.arraycopy()方法:
      System.arraycopy(sourceArray, 0, destArray, 0, length);
      
    • 使用Arrays.copyOf()方法:
      int[] newArray = Arrays.copyOf(originalArray, newLength);
      
  • 排序与查找
    • 使用Arrays.sort()进行排序:
      Arrays.sort(array);
      
    • 使用Arrays.binarySearch()进行二分查找:
      int index = Arrays.binarySearch(array, key);
      

6. 多维数组

  • 定义与使用:多维数组是数组的数组,可以是二维、三维等。例如:
int[][] twoDimArray = {{1, 2, 3},{4, 5, 6},{7, 8, 9}
};

    多维数组的高级使用

    • 不规则数组:可以创建不规则的多维数组(如“锯齿状”数组),例如:
    int[][] jaggedArray = new int[3][];
    jaggedArray[0] = new int[2]; // 第一行有2个元素
    jaggedArray[1] = new int[3]; // 第二行有3个元素
    jaggedArray[2] = new int[1]; // 第三行有1个元素
    

    7. 数组的内存模型

    • 内存布局:数组在内存中是连续的,每个元素占用固定的字节数(根据数据类型),这使得数组在访问时具有很高的效率。
    • 栈与堆:数组变量的引用存储在栈中,而数组本身存储在堆中。

    8. 数组与集合的对比

    • 大小可变性:数组大小固定,而集合(如ArrayList)大小可变。
    • 性能:数组在访问速度上更快,但集合提供了更丰富的功能(如动态扩展、插入、删除等)。

    9. 数组的克隆

    • 浅拷贝与深拷贝:使用clone()方法进行浅拷贝,注意对对象数组的深拷贝需要手动实现。
      int[] clonedArray = originalArray.clone();
      

    10. 注意事项

    • 空数组:可以创建一个长度为0的数组,但不能访问其元素。
    • 数组越界:访问数组时如果索引超出范围,会抛出ArrayIndexOutOfBoundsException异常。
    • 性能:在处理大量数据时,考虑数组的内存使用和访问效率。

    ✅总结:

    数组是Java中最基本的数据结构之一,掌握数组的创建、访问、操作以及其特性,对编写高效的Java程序至关重要。理解数组的局限性和适用场景,可以帮助开发者在实际开发中做出更好的选择。


    2️⃣相关的面试问题:

    1. Java 中数组是对象吗?数组的类型是什么?

    参考回答:
    是的,Java 中的数组是对象,继承自 Object 类。数组的类型由元素类型和维度共同决定。例如,int[] 是一种对象类型,String[][] 是另一种。

    2. 数组和 ArrayList 有什么区别?

    参考回答:

    特性数组 (int[])ArrayList
    长度固定动态扩容
    类型基本类型/对象只能存对象
    性能更快(没有装箱)相对慢
    API 支持丰富的 API

    3. 如何声明并初始化一个长度为 10 的 int 数组,所有值为 42?

    参考回答:

    int[] arr = new int[10]; Arrays.fill(arr, 42);

    4. 下面代码会不会抛出异常?

    int[] arr = new int[5]; System.out.println(arr[5]);

    参考回答:
    会抛出 ArrayIndexOutOfBoundsException,合法索引范围是 04

    5. 如何实现数组的反转?时间复杂度是多少?

    参考回答:

    for (int i = 0, j = arr.length - 1; i < j; i++, j--) { 
    int temp = arr[i]; 
    arr[i] = arr[j];
    arr[j] = temp; 
    }

    时间复杂度是 O(n)。

    6. 对一个数组排序,你会选择哪种方式?为什么?

    参考回答:

    • 小数组:使用 Arrays.sort(arr),底层是 Dual-Pivot QuickSort(对 primitives)或 TimSort(对对象数组);

    • 大数组或需要稳定性:用 TimSort 更好(Collections.sort() 背后);

    • 并行:可以考虑 Arrays.parallelSort()

    7. Java 数组在内存中是如何存储的?

    参考回答:

    • Java 数组在堆中分配;

    • 数组对象包含:数组长度(metadata)+ 元素区域;

    • 通过索引偏移寻址访问,访问速度快(连续内存);

    • 多维数组其实是数组的数组(非真正的矩阵);


    8. 数组的 clone() 是浅拷贝还是深拷贝?

    参考回答:
    是浅拷贝。如果是基本类型数组,复制的是值;如果是对象数组,复制的是引用。

    9. Arrays.asList() 有什么坑?

    参考回答:

    List<Integer> list = Arrays.asList(1, 2, 3); list.add(4); // 抛异常

    • 它返回的是固定大小的 List(内部是 Arrays$ArrayList),不能增删元素;

    • 修改原数组会影响 List,反之亦然。


    10. Java 中如何实现不定长数组?

    参考回答:
    使用集合类,如 ArrayList,因为数组本身长度不可变。如果必须用数组,可以自己封装扩容逻辑(比如每次扩容为 1.5 倍)来模拟。

    11. 手写一个方法:找出数组中只出现一次的元素(其他元素出现两次)

    参考回答:

    public int findSingle(int[] nums) { int result = 0; for (int num : nums) { result ^= num; // 异或操作 } return result; 
    }

    12. 如何用数组实现一个栈/队列?

    参考回答:
    封装一个类,维护一个指针(top/head/tail),配合数组读写实现。面试时可用 ArrayDeque 替代,但手写可以展示底层能力。


    13. 实现一个 O(1) 的获取数组最大值结构(辅助栈思路)

    这个题可以作为数组+栈+算法的综合考察,适合中高级工程师。

    思路

    我们将使用两个数据结构:

    1. 主栈:用于存储实际的数组元素。
    2. 辅助栈:用于存储当前的最大值。当我们向主栈中添加元素时,我们会检查这个元素是否大于或等于辅助栈的栈顶元素,如果是,就将其压入辅助栈。这样,辅助栈的栈顶始终保持当前的最大值。

    操作

    • push(x):将元素 x 压入主栈,并根据需要更新辅助栈。
    • pop():从主栈中弹出元素,如果弹出的元素等于辅助栈的栈顶元素,则同时从辅助栈中弹出。
    • getMax():返回辅助栈的栈顶元素,即当前的最大值。
    
    import java.util.Stack;class MaxStack {public static void main(String[] args) {MaxStack maxStack = new MaxStack();maxStack.push(1);maxStack.push(3);maxStack.push(2);System.out.println(maxStack.getMax()); // 输出 3maxStack.pop();System.out.println(maxStack.getMax()); // 输出 3maxStack.pop();System.out.println(maxStack.getMax()); // 输出 1}private Stack<Integer> mainStack;private Stack<Integer> maxStack;public MaxStack() {mainStack = new Stack<>();maxStack = new Stack<>();}public void push(int x) {mainStack.push(x);// 如果 maxStack 为空,或者 x 大于等于 maxStack 的栈顶元素,则将 x 压入 maxStackif (maxStack.isEmpty() || x >= maxStack.peek()) {maxStack.push(x);}}public void pop() {if (mainStack.isEmpty()) {return;}int poppedValue = mainStack.pop();// 如果弹出的元素等于 maxStack 的栈顶元素,则同时弹出 maxStackif (poppedValue == maxStack.peek()) {maxStack.pop();}}public int top() {if (mainStack.isEmpty()) {throw new IllegalStateException("Stack is empty");}return mainStack.peek();}public int getMax() {if (maxStack.isEmpty()) {throw new IllegalStateException("Stack is empty");}return maxStack.peek();}
    }
    

    📌 Part 1: 基于算法的数组面试题

    1. 找出数组中只出现一次的数字(其余出现两次)
    • 描述:给定一个整数数组,除一个元素外每个元素都出现两次,找出只出现一次的元素。

    • 参考回答:使用异或操作。

    public int findSingle(int[] nums) {int result = 0;for (int num : nums) {result ^= num;}return result;
    }
    
    2. 数组中两个数的和等于目标值
    • 参考回答:使用哈希表存储已访问元素。

    public int[] twoSum(int[] nums, int target) {Map<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; i++) {int complement = target - nums[i];if (map.containsKey(complement)) {return new int[] { map.get(complement), i };}map.put(nums[i], i);}throw new IllegalArgumentException("No solution");
    }
    
    3. 移动零
    • 参考回答:双指针方法。

    public void moveZeroes(int[] nums) {int index = 0;for (int num : nums) {if (num != 0) nums[index++] = num;}while (index < nums.length) {nums[index++] = 0;}
    }
    
    4. 最大子数组和
    • 参考回答:Kadane 算法。

    public int maxSubArray(int[] nums) {int maxSoFar = nums[0], currentMax = nums[0];for (int i = 1; i < nums.length; i++) {currentMax = Math.max(nums[i], currentMax + nums[i]);maxSoFar = Math.max(maxSoFar, currentMax);}return maxSoFar;
    }
    
    5. 数组旋转
    • 参考回答:三次翻转。

    public void rotate(int[] nums, int k) {k %= nums.length;reverse(nums, 0, nums.length - 1);reverse(nums, 0, k - 1);reverse(nums, k, nums.length - 1);
    }
    private void reverse(int[] nums, int start, int end) {while (start < end) {int temp = nums[start];nums[start++] = nums[end];nums[end--] = temp;}
    }
    
    6. 数组去重(原地去重)
    • 参考回答:双指针。

    public int removeDuplicates(int[] nums) {if (nums.length == 0) return 0;int i = 0;for (int j = 1; j < nums.length; j++) {if (nums[j] != nums[i]) {nums[++i] = nums[j];}}return i + 1;
    }
    
    7. 合并两个有序数组
    • 参考回答:逆向双指针。

    public void merge(int[] nums1, int m, int[] nums2, int n) {int i = m - 1, j = n - 1, k = m + n - 1;while (j >= 0) {nums1[k--] = (i >= 0 && nums1[i] > nums2[j]) ? nums1[i--] : nums2[j--];}
    }
    
    8. 寻找数组的第 K 大元素
    • 参考回答:快速选择算法(或堆排序)。

    public int findKthLargest(int[] nums, int k) {PriorityQueue<Integer> heap = new PriorityQueue<>();for (int num : nums) {heap.offer(num);if (heap.size() > k) heap.poll();}return heap.peek();
    }
    
    9. 二维数组查找(行列有序)
    • 参考回答:从右上角开始搜索。

    public boolean searchMatrix(int[][] matrix, int target) {int row = 0, col = matrix[0].length - 1;while (row < matrix.length && col >= 0) {if (matrix[row][col] == target) return true;else if (matrix[row][col] > target) col--;else row++;}return false;
    }
    
    10. 二维数组旋转(原地)
    • 参考回答:先转置,再水平翻转。

    public void rotate(int[][] matrix) {int n = matrix.length;for (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {int temp = matrix[i][j];matrix[i][j] = matrix[j][i];matrix[j][i] = temp;}}for (int i = 0; i < n; i++) {for (int j = 0; j < n / 2; j++) {int temp = matrix[i][j];matrix[i][j] = matrix[i][n - 1 - j];matrix[i][n - 1 - j] = temp;}}
    }
    

    📌 Part 2: 基于 JVM 原理的数组面试题(参考回答)

    1. JVM 中数组如何分配内存?
    • 数组在堆中分配,包含对象头、类型信息和长度字段,然后是连续的元素内存。

    2. 数组与对象在内存布局上的异同?
    • 都继承自 Object;数组有长度字段,数组元素类型可为基本类型或引用类型。

    3. 为何数组越界是运行时异常?
    • JVM 自动插入边界检查指令,防止非法访问。

    4. 数组长度为何不可变?
    • 数组创建时在堆中分配固定空间,长度信息存储在数组对象中。

    5. JVM 优化数组访问的方式?
    • 使用偏移地址访问(base + index * scale);JIT 会做 bounds check elimination 优化。

    6. 为何数组可以存放基本类型而泛型不行?
    • 泛型存在类型擦除问题,而数组是 reifiable 的,必须在运行时保留类型信息。

    7. 多维数组在内存中的表示?
    • 实际是数组的数组,元素不保证物理连续。

    8. Arrays.asList() 与原数组关系?
    • 返回的是固定大小的 List,内部使用原数组,修改会相互影响。

    9. clone() 是浅拷贝还是深拷贝?
    • 是浅拷贝,仅复制引用,不复制对象内容。

    10. 使用 Unsafe 或反射操作数组?
    • 可使用 Unsafe.getInt(array, offset)Array.get(array, index) 操作任意数组元素。

    相关文章:

  • [java八股文][Java并发编程面试篇]场景
  • 自研MCU芯片闪存驱动的实现:OpenOCD详细过程记录与操作指南
  • 关于vue-office在vue3工程中的引用报错问题
  • 京东京言-AI项目实现逻辑
  • windows 部署 Kafka3.x KRaft 模式 不依赖 ZooKeeper
  • [计算机科学#10]:早期的计算机编程方式
  • 立式筒仓式发酵槽实验台试验装置
  • Java网络编程:理解URI、URL和URN
  • 全球森林数据如何分析?基于R语言森林生态系统结构、功能与稳定性分析与可视化
  • 加湿器WT588F02B-8S语音芯片应用方案:让加湿器会说话
  • 云平台搭建
  • 微服务不注册到nacos的方法
  • SpringBoot的自动配置和起步依赖原理
  • Android开发报错解决
  • 正则表达式实战指南:原理、口诀与高频场景案例
  • 姚琛全新特别版EP上线 携手金牌制作人诠释夏日浪漫
  • Python 基础知识
  • 正则表达式(Regular Expression)详解
  • 配置VS的DLL文件引用目录(两种方案,无需每次生成dll后手动将其复制到exe目录下)
  • 表面等离子共振技术(SPR)在小分子与蛋白质相互作用研究中的应用
  • 中华人民共和国和俄罗斯联邦在纪念中国人民抗日战争、苏联伟大卫国战争胜利和联合国成立80周年之际关于进一步深化中俄新时代全面战略协作伙伴关系的联合声明
  • 城管给商户培训英语、政银企合作纾困,上海街镇这样优化营商环境
  • 两部门发布外汇领域行刑反向衔接案例,织密金融安全“防护网”
  • 太原一高中生指出博物馆多件藏品标识不当,馆方已邀请他和专家共同探讨
  • 高进华“控股”后首份年报出炉,史丹利账上可动资金大幅缩水
  • 万里云端遇见上博--搭乘“上博号”主题飞机体验记