Java学习——数组

张开发
2026/4/11 18:53:39 15 分钟阅读

分享文章

Java学习——数组
目录一、基本概念1、数组的声明2、数组创建3、数组赋值4、数组的使用5、数组的特点二、内存分析1、一维数组2、二维数组三、数组的遍历1、一维数组的遍历方式2、多维数组的遍历方式四、数组初始化1、静态初始化2、动态初始化3、默认初始化4、总结五、Arrays工具类1、常用方法概览2、典型代码示例3、注意事项4、与 Collection 工具类对比5、总结一、基本概念数组就是一个容器用来存一批同种类型的数据。数组描述的是相同类型的若干个数据按照一定的先后次序排列组合而成。其中每一个数据称作一个元素每个元素可以通过一个索引下标来访问它们。1、数组的声明一维数组数组的语法格式数据类型 [ ] 数组名; //格式一 数据类型 数组名 [ ]; //格式二示例// 推荐写法类型与变量名分明 int[] arr1; // 一维 int 数组 String[] names; // 一维 String 数组 // 不推荐C 风格但语法允许 int arr2[];二维数组数组的语法格式数据类型 [] [] 数组名; //格式一 数据类型 数组名 [] []; //格式二 数据类型 [] 数组名 []; //格式三示例// 推荐写法 int[][] matrix; int[][] scores; // 其他合法写法不推荐 int matrix[][]; int[] matrix[];2、数组创建一维数组创建指定长度空间的数组语法格式如下数组类型 [ ] 数组名; 数组名 new 数组类型 [ 数组长度 ]数组的声明和创建合成一步语法格式如下数组类型 数组名 [ ] new 数组类型 [ 数组长度 ]; //格式一 数组类型 [ ] 数组名 new 数组类型 [ 数组长度 ]; //格式二二维数组动态创建指定行数和列数int[][] arr new int[3][4]; // 3行4列所有元素默认值为0注解1第一个[3]表示有 3 个一维数组行2第二个[4]表示每个一维数组有 4 个元素列3内存中栈中变量arr存储堆中二维数组对象的引用堆中二维数组对象包含 3 个引用分别指向 3 个长度为 4 的一维数组。​​​​​​动态创建只指定行数列数后续单独指定int[][] arr new int[3][]; // 只确定有3行每行暂时为null arr[0] new int[2]; // 第0行有2列 arr[1] new int[5]; // 第1行有5列 arr[2] new int[3]; // 第2行有3列注解这种方式允许不规则数组每行列数可以不同直接赋值int[][] arr { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };注解相当于创建了一个 3×3 的二维数组。3、数组赋值为数组每个元素进行赋值语法格式如下数组名 [ 元素索引/下标 ] 数值;* 数组索引越界会出现异常 *4、数组的使用数组中已经存储元素后可以使用指定的元素按照元素索引对数组元素进行访问即可。访问格式数组名 [ 元素索引/下标 ]数组的长度属性length5、数组的特点一维数组特点说明类型一致所有元素必须是同一种数据类型长度固定创建后长度不可变连续内存元素在内存中连续存储通过索引快速访问引用类型数组本身是对象继承Object零基索引第一个元素索引为0最后一个为length-1即最大元素个数为 n 最后一个元素索引为 n-1数组是固定长度、同类型、连续存储、索引高效的线性数据结构。二维数组二维数组是数组的数组支持规则矩形和锯齿形状。二维数组遍历时注意每行的长度可能不同用arr[i].length获取列数。二维数组打印用Arrays.deepToString()比较用Arrays.deepEquals()。二维数组常用于矩阵运算、表格数据、棋盘游戏等场景。注意事项说明行索引和列索引均从0开始访问arr[2][3]需要行数≥3且该行列数≥4每行长度可能不同用arr[i].length获取第i行的列数不要假设所有行等长初始化锯齿数组先分配行再逐行分配列打印多维数组使用Arrays.deepToString()不能用toString()空指针风险若某行未初始化null访问该行元素会抛NullPointerException内存占用二维数组总元素 行数 × 平均列数但每个一维数组还有对象头开销一维数组与二位数组的对比维度声明长度访问内存一维int[] aa.lengtha[i]单一连续块二维int[][] aa.length行数a[i].length列数a[i][j]多个连续块行数组数据数组二、内存分析Java 中的数组是引用类型在内存中涉及栈Stack和堆Heap两个区域。理解数组的内存模型有助于掌握数组的传递、复制及性能特性堆new 出来的东西会在这块内存里开辟空间并产生地址虚拟机栈方法运行时进入的内存1、一维数组代码示例public class ArrayMemory { public static void main(String[] args) { int[] arr new int[3]; arr[0] 10; arr[1] 20; } }内存步骤分析步骤一声明变量int[] arr;在栈中为arr分配空间此时值为null未指向任何对象。栈中分配空间步骤二创建数组对象new int[3]在堆中分配一块连续内存存储数组对象。数组对象包含对象头Mark Word、类型指针、数组长度length属性、元素存储区域。所有元素初始化为默认值int默认为0。返回该对象的起始内存地址假设0x1000。堆分配内存步骤三赋值给变量arr 地址将堆中数组对象的地址0x1000存入栈变量arr。地址存入变量步骤四元素赋值arr[0] 10; arr[1] 20;通过地址找到堆中的数组对象根据索引偏移量计算元素位置写入值。以地址进行对象赋值注意length是数组对象的一个成员变量不是方法所以访问arr.length不需要括号2、二维数组示例代码public class ArrayMemory { public static void main(String[] args) { int[][] arr new int[2][3]; arr[0][1] 5; } }内存步骤分析步骤一声明变量arr在栈内存中为变量arr分配空间。此时arr是一个引用变量值为null未指向任何对象。栈内分配空间步骤二执行new int[2][3]– 创建外层数组对象在堆内存中分配一个一维数组对象类型为int[][]即元素类型为int[]的数组。该数组的长度为2行数存储两个元素每个元素都是int[]类型的引用。所有元素初始化为默认值null因为引用类型的默认值是null。该数组对象在堆中的假设地址为0x1000。堆内分配行对象空间步骤三为外层数组的每个元素创建内层数组new int[2][3]语法会自动为每一行创建对应长度的内层数组规则矩形。为第 0 行创建int[3]数组地址假设0x2000所有元素默认0。为第 1 行创建int[3]数组地址假设0x3000所有元素默认0。将这两个内层数组的地址赋值给外层数组的[0]和[1]位置。堆内创建内层数组步骤四将外层数组地址赋值给arr将堆中外层数组的地址0x1000存入栈变量arr。外层数组地址赋值给变量步骤五执行赋值语句arr[0][1] 5;解析arr[0]通过arr找到外层数组地址0x1000取出索引0处的元素即内层数组的地址0x2000。解析[1]在地址0x2000的内层数组中找到索引1的位置。赋值将5写入该位置。其他内存区域不变。执行赋值语句进程总结二维数组是数组的数组外层数组存储内层数组的引用。规则矩形new int[m][n]一次性分配外层和内层内层长度相同。锯齿数组先分配外层再逐行分配内层每行长度可不同。内存位置所有数组对象都在堆中栈中变量仅保存外层数组的地址。访问元素通过两次解引用arr[i][j]先找到内层数组再定位元素。默认值外层数组元素默认null内层数组元素默认0/false/null等。三、数组的遍历数组遍历是指按顺序访问数组中的每一个元素通常用于读取、修改或计算元素。Java 中提供了多种遍历数组的方式适用于不同场景。1、一维数组的遍历方式普通 For 循环最灵活的方式可以控制索引支持修改元素。int[] arr {10, 20, 30, 40}; for (int i 0; i arr.length; i) { System.out.println(arr[ i ] arr[i]); // arr[i] arr[i] * 2; // 修改元素 }增强 For 循环语法简洁但只能读取元素不能修改元素值除非元素是引用类型且修改其属性。不能获取当前索引。int[] arr {10, 20, 30, 40}; for (int value : arr) { System.out.println(value); // value value * 2; // 不影响原数组 }While 循环适合需要更复杂循环控制如动态改变索引的场景。int[] arr {10, 20, 30, 40}; int i 0; while (i arr.length) { System.out.println(arr[i]); i; }Do - While 循环至少执行一次循环体较少用于数组遍历。int[] arr {10, 20, 30, 40}; int i 0; do { System.out.println(arr[i]); i; } while (i arr.length);使用Arrays.toString()快速打印适合调试直接输出数组内容为字符串。import java.util.Arrays; int[] arr {10, 20, 30}; System.out.println(Arrays.toString(arr)); // [10, 20, 30]2、多维数组的遍历方式嵌套 For 循环int[][] matrix { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; for (int i 0; i matrix.length; i) { for (int j 0; j matrix[i].length; j) { System.out.print(matrix[i][j] ); } System.out.println(); }嵌套增强 For 循环for (int[] row : matrix) { for (int value : row) { System.out.print(value ); } System.out.println(); }使用Arrays.deepToString()打印多维数组System.out.println(Arrays.deepToString(matrix)); // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]注意事项数组下标越界遍历时索引必须0 ≤ i arr.length否则出Array Index Out Of Bounds Exception。增强for不能修改基本类型数组的元素因为循环变量是局部副本。引用类型数组的增强for可以修改对象属性遍历过程中不建议删除或添加元素数组长度固定不存在增删若使用集合注意并发修改。空指针检查若数组引用为null遍历前应先判断否则抛出NullPointerException。总结普通for功能最全适合需要索引或修改元素的场景。增强for代码简洁适合只读遍历。while/do-while适合配合其他条件或复杂逻辑。Arrays.toString()/deepToString()快速打印调试。四、数组初始化数组初始化是指在创建数组对象时为数组元素分配内存空间并赋予初始值的过程。Java 中的数组初始化分为动态初始化、静态初始化和默认初始化其实动态初始化后会自动进行默认初始化。1、静态初始化在定义数组的同时就为数组元素进行分配空间并赋值。语法结构如下数组元素类型[] 数组名 {值1, 值2, ...}; //法一 数组元素类型[] 数组名 new 数组元素类型[]{值1, 值2, ...}; //法二特点在声明的同时显式指定元素值由编译器推断长度。注意简洁写法不能先声明再赋值。2、动态初始化数组定义与为数组元素分配空间并赋值的操作分开进行。定义数组时先不存入具体的元素值只确定数组存储的数据类型和数组的长度。语法结构如下数组元素类型[] 数组名 new 数组元素类型[长度];特点先分配空间系统自动赋予默认值之后再手动赋值。默认值规则动态分配时自动赋值表数组类型默认值byte,short,int,long0float,double0.0char\u0000空字符booleanfalse引用类型类、接口、数组null3、默认初始化数组是引用类型他的元素相当于类的实例变量因此数组一经分配空间其中的每个元素也被按照实例变量同样的方式被隐式初始化。也就是说数组有默认的初始化值。数组元素默认初始值数组元素类型默认值byte0short0int0long0Lfloat0.0fdouble0.0dchar\u0000空字符不可见booleanfalse引用类型类、接口、数组、String等null说明上述默认值适用于所有维度的数组如一维、二维或多维。默认初始化发生在动态分配内存之后、显式赋值之前。局部变量数组本身需要显式初始化通过new或静态初始化但其元素在new之后已经拥有默认值。char的默认值\u0000在打印时通常显示为空白或不可见字符。long的默认值0L中的L仅表示字面量类型实际存储为数值0。4、总结注意事项长度不能为负数new int[-5]会抛出NegativeArraySizeException。静态初始化不能同时指定长度int[3] arr {1,2,3};❌ 错误长度由元素个数决定。数组元素访问前必须确保数组已初始化且不为 null否则NullPointerException。动态初始化后元素值不是“空”基本类型有默认值引用类型为null而不是未定义状态。匿名数组可以在方法调用中直接创建并传递数组无需变量。二维数组动态初始化用new int[m][n]或先new int[m][]再逐行new。二维数组静态初始化用{{...}, {...}}形式。初始化的特点初始化方式语法时机特点动态初始化new type[size]运行时先分配空间默认值后赋值静态初始化{val1,val2,...}编译时直接指定元素值长度自动推断默认初始化系统自动进行对象创建时基本类型默认0/0.0/false引用类型null初始化的应用场景应用场景推荐初始化方式说明与示例已知固定元素值如星期几、月份天数、颜色常量静态初始化数据在编写代码时已经确定直接写在大括号中。int[] daysInMonth {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};长度确定但元素值需动态计算或从外部输入如用户输入、数据库查询结果动态初始化先分配空间再通过循环或逐个赋值填入数据。int[] scores new int[30];brfor (int i 0; i scores.length; i) { scores[i] scanner.nextInt(); }需要默认占位值如缓存数组、布尔标记数组、待填充的数据缓冲区默认初始化通过动态初始化自动实现利用数组创建后元素自动为 0 / false / null 的特性后续按需修改。boolean[] visited new boolean[n]; // 全部 falsebrint[] buffer new int[1024]; // 全部 0方法需要返回多个值如统计数据、坐标点集合静态或动态初始化在方法内创建数组并返回调用方接收后遍历使用。int[] getMinMax(int[] arr) { return new int[]{min, max}; }参数为可变数量的同类数据如计算任意多个数的和动态初始化结合可变参数可变参数本质就是数组由编译器自动初始化。int sum(int... nums) { int s0; for(int n:nums) sn; return s; }多维数组矩阵、表格数据嵌套静态初始化已知数据或嵌套动态初始化未知数据规则矩形常用new int[3][4]锯齿数组先分配行再逐行分配列。int[][] matrix {{1,2},{3,4,5}};临时存储少量固定数据如测试用例、示例数据匿名数组直接作为参数传递无需声明变量。printArray(new int[]{1, 2, 3});静态初始化数据已知、固定不变 → 代码简洁直观。动态初始化长度已知内容未知或需动态计算 → 灵活性高。默认初始化需要零值或空值占位 → 减少手动赋值代码。匿名数组一次性使用 → 节省变量命名。五、Arrays工具类java.util.Arrays是 Java 提供的用于操作数组的工具类包含各种静态方法如排序、搜索、填充、比较、转换为字符串等。使用前需导入import java.util.Arrays;Arrays工具类对应各版本的API使用说明文档的链接如下Java 8https://docs.oracle.com/javase/8/docs/api/Java 11https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Arrays.htmlJava 17https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Arrays.htmlJava 21https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Arrays.htmlJava 25https://apidia.net/java/OpenJDK/25/java.util.Arrays.html1、常用方法概览方法作用toString(arr)一维数组转字符串格式deepToString(arr)多维数组转字符串sort(arr)对数组升序排序sort(arr, from, to)对指定范围排序binarySearch(arr, key)二分查找数组必须先排序equals(arr1, arr2)比较一维数组是否相等deepEquals(arr1, arr2)比较多维数组是否相等fill(arr, val)用 val 填充整个数组fill(arr, from, to, val)填充指定范围copyOf(arr, newLength)复制数组可截断或补默认值copyOfRange(arr, from, to)复制指定范围asList(T... a)将数组转换为List固定长度parallelSort(arr)并行排序大数据量时性能好setAll(arr, generator)使用生成器函数设置每个元素stream(arr)返回数组的IntStream/DoubleStream等2、典型代码示例打印数组int[] arr {5, 2, 8, 1, 9}; System.out.println(Arrays.toString(arr)); // [5, 2, 8, 1, 9] int[][] matrix {{1,2},{3,4}}; System.out.println(Arrays.deepToString(matrix)); // [[1, 2], [3, 4]]排序与二分查找int[] arr {5, 2, 8, 1, 9}; Arrays.sort(arr); // [1, 2, 5, 8, 9] int index Arrays.binarySearch(arr, 5); // 2存在 int notFound Arrays.binarySearch(arr, 10); // -6插入点 -1填充int[] arr new int[5]; Arrays.fill(arr, 100); // [100, 100, 100, 100, 100] Arrays.fill(arr, 1, 4, 0); // [100, 0, 0, 0, 100]复制数组int[] original {1, 2, 3, 4, 5}; int[] copy Arrays.copyOf(original, 3); // [1, 2, 3]截断 int[] copy2 Arrays.copyOf(original, 7); // [1,2,3,4,5,0,0]补默认值 int[] range Arrays.copyOfRange(original, 1, 4); // [2, 3, 4]比较数组int[] a {1, 2, 3}; int[] b {1, 2, 3}; int[] c {1, 2, 4}; System.out.println(Arrays.equals(a, b)); // true System.out.println(Arrays.equals(a, c)); // false // 多维比较 int[][] m1 {{1,2},{3,4}}; int[][] m2 {{1,2},{3,4}}; System.out.println(Arrays.deepEquals(m1, m2)); // true数组转 List注意返回的 List 是固定长度的不能增删String[] strs {A, B, C}; ListString list Arrays.asList(strs); // list.add(D); // 抛出 UnsupportedOperationException list.set(1, X); // 允许修改元素原数组也会改变 System.out.println(Arrays.toString(strs)); // [A, X, C]使用setAll生成数组int[] arr new int[5]; Arrays.setAll(arr, i - i * i); // [0, 1, 4, 9, 16]并行排序大数据量时性能更好int[] bigArr new int[10_000_000]; // 填充随机数... Arrays.parallelSort(bigArr); // 利用 Fork/Join 框架3、注意事项要点说明binarySearch前必须排序否则结果不确定asList返回的 List 不支持add/remove因为底层仍是数组长度固定equals与deepEquals一维用equals多维用deepEquals原始类型数组无法直接转ListInteger需循环或使用Arrays.stream(arr).boxed().collect(...)修改asList返回的 List 会影响原数组因为是“视图”排序算法sort对原始类型使用双轴快速排序对对象使用 TimSort4、与 Collection 工具类对比工具类操作对象常见方法Arrays数组sort,binarySearch,toStringCollections集合List/Set/Queuesort,binarySearch,reverse,shuffle两者方法名类似但作用对象不同。5、总结Arrays是操作数组的静态工具类提供了排序、搜索、填充、复制、转换等常用功能。配合toString/deepToString可方便打印数组内容。asList是数组与集合之间的桥梁但返回的 List 不可变长度。在 Java 8 中Arrays.stream()可将数组转为流式处理更函数式。

更多文章