数组的初始化

一维数组

在 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    // 复制的长度
);
  1. 本地方法native

    • 用 C/C++ 实现,不在 Java 源码中
    • 直接调用操作系统底层功能
  2. 速度最快

    • 因为是本地方法,避免了 Java 虚拟机的一些开销
    • 内存复制效率极高
  3. 需要预先分配目标数组

    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()
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐