【Java SE】数组
数组的初始化
一维数组
在 Java 中,可以使用 java.util.Arrays 类提供的 fill 方法来快速初始化或填充数组。
1. 填充整个数组
将数组中的所有元素统一设置为指定的值。
int[] ret = new int[10];
// 将数组 ret 的所有元素填充为 -1
Arrays.fill(ret, -1);
System.out.println(Arrays.toString(ret));
说明: Arrays.fill(ret, -1) 会把 ret 数组长度范围内的每一个位置都赋值为 -1。
2. 填充指定范围
仅将数组中从起始索引(包含)到结束索引(不包含)的部分填充为指定的值。
int[] ret = new int[10];
// 将数组 ret 从索引 1 开始到索引 3(不包含 3)的部分填充为 -1
Arrays.fill(ret, 1, 3, -1);
System.out.println(Arrays.toString(ret));
说明:
fromIndex: 1:起始索引(包含)。toIndex: 3:结束索引(不包含)。val: -1:要填充的值。- 执行后,只有
ret[1]和ret[2]会被赋值为-1,其余位置保持默认值(0)。
二维数组
对于二维数组,Arrays.fill() 不能直接完成所有元素的填充,因为它会将每一“行”视为一个对象。正确的方式是遍历每一行,然后对每一行(它本身是一个一维数组)分别使用 Arrays.fill()。
1. 填充所有元素
int[][] matrix = new int[3][4];
// 遍历二维数组的每一行
for (int[] row : matrix) {
// 将当前行(一维数组)的所有元素填充为 -1
Arrays.fill(row, -1);
}
System.out.println(Arrays.deepToString(matrix));
说明:
- 创建了一个 3 行 4 列的二维数组
matrix。 - 通过
for循环遍历matrix的每一行row。 - 对每一个
row(它是一个一维数组)调用Arrays.fill(row, -1)进行填充。 - 对于二维数组的打印,使用
Arrays.deepToString()可以得到更易读的格式。
2. 使用 for 循环填充
也可以使用传统的嵌套 for 循环来完成初始化,这种方式在逻辑上更直接。
int[][] matrix = new int[3][4];
for (int i = 0; i < matrix.length; i++) { // 遍历行
for (int j = 0; j < matrix[i].length; j++) { // 遍历列
matrix[i][j] = -1;
}
}
System.out.println(Arrays.deepToString(matrix));
数组的创建
1. 声明数组
声明一个数组变量,告诉编译器这个变量将用于引用一个数组。
语法:
// 推荐方式
dataType[] arrayName;
// 或者 (C/C++ 风格,但不推荐在 Java 中使用)
dataType arrayName[];
示例:
// 声明一个整型数组
int[] myArray;
// 声明一个字符串数组
String[] names;
// 声明一个二维整型数组
int[][] myMatrix;
说明: 仅仅声明数组并不会在内存中创建数组,此时 myArray 的值为 null。
2. 分配内存并创建数组
使用 new 关键字来实际创建数组并为其分配内存。
一维数组
a) 指定长度创建
创建数组时指定它的大小,数组中的元素会被初始化为默认值。
| 类型 | 默认值 |
|---|---|
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0 |
| float | 0.0f |
| double | 0.0 |
| char | /u0000 |
| boolean | false |
语法与示例:
// 创建一个长度为 5 的整型数组
int[] myArray = new int[5];
// 此时数组内容为: [0, 0, 0, 0, 0]
// 创建一个长度为 10 的字符串数组
String[] names = new String[10];
// 此时数组内容为: [null, null, null, null, null, null, null, null, null, null]
b) 创建时直接初始化(静态初始化)
在创建数组的同时为其提供初始值。编译器会自动计算数组的长度。
语法与示例:
// 创建一个整型数组并提供初始值
int[] myArray = {10, 20, 30, 40, 50};
// 编译器会自动推断其长度为 5
// 也可以写成:
int[] anotherArray = new int[]{100, 200, 300};
二维数组
a) 指定维度创建
创建时指定行数和列数。
语法与示例:
// 创建一个 3 行 4 列的二维整型数组
int[][] matrix = new int[3][4];
// 所有 12 个元素都被初始化为 0
b) 创建时直接初始化(静态初始化)
语法与示例:
// 创建一个二维数组并提供初始值
int[][] matrix = {
{1, 2, 3}, // 第 0 行
{4, 5, 6}, // 第 1 行
{7, 8, 9} // 第 2 行
};
// 这是一个 3x3 的矩阵
c) 创建不规则数组(Jagged Array)
在 Java 中,二维数组本质上是“数组的数组”。因此,你可以只指定行数,然后为每一行分别创建不同长度的数组。
语法与示例:
// 1. 只指定行数
int[][] jaggedArray = new int[3][];
// 2. 为每一行分别指定不同的列数
jaggedArray[0] = new int[4]; // 第 0 行有 4 个元素
jaggedArray[1] = new int[2]; // 第 1 行有 2 个元素
jaggedArray[2] = new int[5]; // 第 2 行有 5 个元素
// 你也可以在初始化时直接定义不规则数组
int[][] anotherJagged = {
{1, 2},
{3, 4, 5},
{6}
};
数组的遍历与访问
一维数组
a) 普通 for 循环
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
b) 增强 for 循环(for-each)
for (int x : array) {
System.out.print(x + " ");
}
c) Arrays.toString() 方法
String ret = Arrays.toString(array);
System.out.println(ret);
二维数组
a) 嵌套普通 for 循环
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
System.out.print(array[i][j] + " ");
}
}
array.length是行数,array[i].length是第 i 行的列数
b) 嵌套增强 for 循环(for-each)
for(int[] tmpArray : array) {
for(int x : tmpArray) {
System.out.print(x + " ");
}
System.out.println();
}
c) Arrays.deepToString() 方法
// 普通 Arrays.toString() 只能处理一维数组
// String ret = Arrays.toString(array); // 错误用法
// 正确的做法
String ret = Arrays.deepToString(array);
System.out.println(ret);
- 输出格式:
[[1, 2, 3], [4, 5, 6]] - 为什么不用 toString():
toString()只能展开第一层,返回类似[I@15db9742的地址信息deepToString()会递归遍历所有维度
Arrays.ToString()与Arrays.deepToString()
int[] myArray = {10, 20, 30, 40, 50};
System.out.println(myArray);
int[][] matrix = {
{1, 2, 3}, // 第 0 行
{4, 5, 6}, // 第 1 行
{7, 8, 9} // 第 2 行
};
String ret2 = Arrays.toString(matrix);
System.out.println(ret2);
String ret1 = Arrays.deepToString(matrix);
System.out.println(ret1);
输出结果:
[I@1b6d3586
[[I@74a14482, [I@1540e19d, [I@677327b6]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
输出格式解析
[:表示这是一个数组类型I:表示数组元素类型是int(Integer 的简写)@:分隔符1b6d3586:对象的哈希码(十六进制表示)(相当于地址)
当直接输出 System.out.println(myArray) 时,实际上调用的是:
// 默认调用的是数组对象的 toString() 方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
不同数组类型的输出前缀
| 数组类型 | 输出前缀 | 示例 |
|---|---|---|
int[] |
[I |
[I@1b6d3586 |
double[] |
[D |
[D@6d06d69c |
String[] |
[Ljava.lang.String; |
[Ljava.lang.String@7852e922 |
byte[] |
[B |
[B@4e25154f |
char[] |
[C |
[C@70dea4e |
“二维数组就是一个特殊的一维数组”
- 二维数组本质上是一个一维数组,其每个元素又是一个一维数组
- 这解释了为什么
for(int[] tmpArray : array)可以工作- 也说明了为什么需要
deepToString()而不是普通的toString()
一维数组的参数传递和内存管理
public static int[] func1(int[] array) {
int[] ret = new int[array.length]; // 在堆上创建新数组
for (int i = 0; i < array.length; i++) {
ret[i] = array[i] * 2; // 每个元素乘以2
}
return ret; // 返回新数组的引用
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
int[] tmp = func1(array);
System.out.println(Arrays.toString(array)); // [1, 2, 3, 4, 5]
System.out.println(Arrays.toString(tmp)); // [2, 4, 6, 8, 10]
}

public static void func2(int[] array) {
for (int i = 0; i < array.length; i++) {
array[i] = array[i] * 2;
}
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
System.out.println(Arrays.toString(array));
func2(array);
System.out.println(Arrays.toString(array));
}

public static void func2(int[] array) {
array = new int[]{1, 8, 7, 6};
}
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
func2(array);
System.out.println(Arrays.toString(array));
}

练习
数组转换为字符串
基础版本
public static String myToString(int[] array) {
String ret = "["; // 1. 以 "[" 开头
for (int i = 0; i < array.length; i++) {
ret += array[i]; // 2. 添加当前元素
if (i != array.length - 1) { // 3. 如果不是最后一个元素
ret += ", "; // 添加逗号和空格分隔符
}
}
ret += "]"; // 4. 以 "]" 结尾
return ret; // 5. 返回结果字符串
}
与官方 Arrays.toString() 对比
| 特性 | 自定义 myToString | Arrays.toString() |
|---|---|---|
| 功能 | ✅ 基本实现 | ✅ 完整实现 |
| 性能 | ❌ 较差(字符串拼接) | ✅ 优化(StringBuilder) |
| 通用性 | ❌ 只支持 int[] | ✅ 所有类型数组 |
| 多维数组 | ❌ 不支持 | ✅ 有 deepToString() |
| null 处理 | ❌ 可能空指针 | ✅ 返回 “null” |
优化版本(使用 StringBuilder)
public static String myToStringBetter(int[] array) {
if (array == null) {
return "null"; // 处理 null 情况
}
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < array.length; i++) {
sb.append(array[i]);
if (i != array.length - 1) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
二分查找
基础版本
private static int myBinarySearch(int[] a, int fromIndex, int toIndex, int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.-(返回值 + 1) 得到插入点
}
完整代码
public class BinarySearchExample {
private static int myBinarySearch(int[] a, int fromIndex, int toIndex, int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
// 无符号右移,相当于 (low + high) / 2,但可以防止溢出
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // 找到key,返回索引
}
return -(low + 1); // 未找到key,返回负的插入点
}
public static void main(String[] args) {
int[] array = {1, 31,4, 2, 15};
System.out.println("排序前的数组: " + Arrays.toString(array));
Arrays.sort(array); // 默认从小到大排序
System.out.println("排序后的数组: " + Arrays.toString(array));
System.out.println("\n=== 自定义二分查找演示 ===");
// 查找存在的元素
int result1 = myBinarySearch(array, 0, array.length, 15);
System.out.println("查找15: " + result1);
// 查找不存在的元素
int result2 = myBinarySearch(array, 0, array.length, 3);
System.out.println("查找3: " + result2);
if (result2 < 0) {
int insertionPoint = -(result2 + 1);
System.out.println("元素3应该插入到索引: " + insertionPoint);
}
}
}
Arrays.binarySearch()
Java 为不同数据类型的数组提供了多个重载的 binarySearch() 方法:
1. 对整个数组进行二分查找
binarySearch(int[] a, int key)
binarySearch(byte[] a, byte key)
binarySearch(char[] a, char key)
binarySearch(short[] a, short key)
binarySearch(long[] a, long key)
binarySearch(float[] a, float key)
binarySearch(double[] a, double key)
- 要求数组必须是已排序的
2. 对数组的指定范围进行二分查找
binarySearch(int[] a, int fromIndex, int toIndex, int key)
binarySearch(byte[] a, int fromIndex, int toIndex, byte key)
binarySearch(char[] a, int fromIndex, int toIndex, char key)
binarySearch(short[] a, int fromIndex, int toIndex, short key)
binarySearch(long[] a, int fromIndex, int toIndex, long key)
binarySearch(float[] a, int fromIndex, int toIndex, float key)
binarySearch(double[] a, int fromIndex, int toIndex, double key)
- 在数组的指定范围
[fromIndex, toIndex)内查找 key - 注意:范围是左闭右开,包含
fromIndex,不包含toIndex
冒牌排序
public static void bubbleSort(int[] array) {
// 外层循环:控制排序的趟数
for (int i = 0; i < array.length-1; i++) {
boolean fg = false; // 优化标志:记录本趟是否发生交换
// 内层循环:进行相邻元素的比较和交换
for (int j = 0; j < array.length-1-i; j++) {
if(array[j] > array[j+1]) { // 如果前一个大于后一个
// 交换相邻元素
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
fg = true; // 标记发生了交换
}
}
// 优化:如果本趟没有发生交换,说明数组已经有序
if(fg == false) {
return; // 提前结束排序
}
}
}
数组逆序
方法一:双指针交换法(最常用)
public static void reverseArray(int[] array) {
int left = 0;
int right = array.length - 1;
while (left < right) {
// 交换左右指针指向的元素
int temp = array[left];
array[left] = array[right];
array[right] = temp;
// 移动指针
left++;
right--;
}
}
方法二:for循环实现
public static void reverseArray(int[] array) {
for (int i = 0; i < array.length / 2; i++) {
int temp = array[i];
array[i] = array[array.length - 1 - i];
array[array.length - 1 - i] = temp;
}
}
方法三:使用Collections工具类(适用于对象数组)
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public static Integer[] reverseArray(Integer[] array) {
List<Integer> list = Arrays.asList(array);
Collections.reverse(list);
return list.toArray(new Integer[0]);
}
方法四:使用Stream API(Java 8+)
import java.util.stream.IntStream;
public static int[] reverseArray(int[] array) {
return IntStream.rangeClosed(1, array.length)
.map(i -> array[array.length - i])
.toArray();
}
数组拷贝
Arrays.copyOf() 与Arrays.copyOfRange()
int[] copy = Arrays.copyOf(array, length * 2);
- 如果新长度 > 原数组长度,多余位置补默认值(数值类型补0,boolean补false,引用类型补null)
- 如果新长度 < 原数组长度,只复制前几个元素
int[] copy = Arrays.copyOfRange(array, from: 1, to: 3);
from:起始索引(包含)to:结束索引(不包含)
int[] array = {10, 20, 30, 40, 50};
// copyOf示例
int[] copy1 = Arrays.copyOf(array, 3); // [10, 20, 30]
int[] copy2 = Arrays.copyOf(array, 7); // [10, 20, 30, 40, 50, 0, 0]
// copyOfRange示例
int[] range1 = Arrays.copyOfRange(array, 1, 4); // [20, 30, 40] (索引1,2,3)
int[] range2 = Arrays.copyOfRange(array, 2, 6); // [30, 40, 50, 0] (超出补0)
System.arraycopy() — 本地方法
public static native void arraycopy(
Object src, // 源数组
int srcPos, // 源数组起始位置
Object dest, // 目标数组
int destPos, // 目标数组起始位置
int length // 复制的长度
);
-
本地方法(
native)- 用 C/C++ 实现,不在 Java 源码中
- 直接调用操作系统底层功能
-
速度最快
- 因为是本地方法,避免了 Java 虚拟机的一些开销
- 内存复制效率极高
-
需要预先分配目标数组
int[] src = {1, 2, 3, 4, 5}; int[] dest = new int[5]; System.arraycopy(src, 0, dest, 0, src.length);
public class ArrayCopyDemo {
public static void main(String[] args) {
int[] source = {1, 2, 3, 4, 5, 6, 7};
int[] dest = new int[5];
// 从source索引2开始复制3个元素到dest索引1开始
System.arraycopy(source, 2, dest, 1, 3);
System.out.println("源数组: " + Arrays.toString(source));
System.out.println("目标数组: " + Arrays.toString(dest));
// 输出: [0, 3, 4, 5, 0]
}
}
两种方式的对比
| 特性 | Arrays.copyOf() / copyOfRange() |
System.arraycopy() |
|---|---|---|
| 实现方式 | Java 方法,内部调用 System.arraycopy() |
本地方法(C/C++) |
| 性能 | 较快 | 最快 |
| 使用便捷性 | 简单,一行代码 | 需要预先创建目标数组 |
| 功能 | 创建新数组并复制 | 复制到已存在的数组 |
| 返回值 | 返回新数组 | void(无返回值) |
| 典型场景 | 数组扩容、截取子数组 | 数组合并、部分复制 |
实际上,Arrays.copyOf() 和 Arrays.copyOfRange() 底层也是调用 System.arraycopy():
// Arrays.copyOf 的简化实现思路
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
- 需要创建新数组 → 用
Arrays.copyOf()或Arrays.copyOfRange() - 需要最高性能 → 用
System.arraycopy() - 需要部分复制到已存在数组 → 用
System.arraycopy() - 需要数组扩容 → 用
Arrays.copyOf()
更多推荐




所有评论(0)