Java 实例 – 删除数组元素(千字长文)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在 Java 编程中,数组是一种基础且常用的线性数据结构。然而,数组的固定长度特性常常让开发者在需要动态增删元素时感到困扰。例如,当需要从数组中删除某个特定元素或指定位置的元素时,开发者必须采用间接的方法实现这一操作。本文将通过Java 实例 – 删除数组元素这一主题,结合具体案例和代码示例,深入解析数组删除操作的核心原理与实现技巧,帮助读者掌握这一重要技能。


理解数组的不可变性

数组的静态特性

Java 中的数组一旦被初始化,其长度就无法直接修改。这意味着,如果我们需要“删除”某个元素,实际上需要通过创建新数组并复制原数组的非目标元素来间接实现。这一过程类似于“搬家”:当需要从一个固定大小的房间中移除物品时,必须将其他物品重新整理到一个更小的新房间中。

示例:尝试直接删除元素的无效代码

int[] arr = {1, 2, 3, 4};  
// 以下代码会引发编译错误,因为数组长度不可变  
arr.length = 3;  

方法一:手动复制法(基础版)

原理与步骤

手动复制法是通过遍历原数组,将需要保留的元素逐个复制到新数组中,从而实现“删除”操作。具体步骤如下:

  1. 创建新数组,长度比原数组少 1(假设删除一个元素);
  2. 遍历原数组,跳过目标元素;
  3. 将非目标元素依次写入新数组。

示例:删除指定索引的元素

public static int[] removeElementByIndex(int[] arr, int index) {  
    if (index < 0 || index >= arr.length) {  
        throw new IndexOutOfBoundsException("索引越界");  
    }  
    int[] newArr = new int[arr.length - 1];  
    for (int i = 0, j = 0; i < arr.length; i++) {  
        if (i != index) {  
            newArr[j++] = arr[i];  
        }  
    }  
    return newArr;  
}  

// 使用示例  
int[] original = {10, 20, 30, 40};  
int[] result = removeElementByIndex(original, 2);  // 删除索引2(元素30)  
System.out.println(Arrays.toString(result));  // 输出:[10, 20, 40]  

优缺点分析

特点描述
优点实现简单,无需依赖额外库;适用于对性能要求不高的场景。
缺点需要手动管理索引,容易出错;时间复杂度为 O(n),效率较低。

方法二:使用 System.arraycopy()

优化复制过程

System.arraycopy() 是 Java 提供的高效数组复制工具,能显著减少手动遍历的代码量。其语法为:

System.arraycopy(src, srcPos, dest, destPos, length);  

其中:

  • src:源数组;
  • srcPos:源数组的起始位置;
  • dest:目标数组;
  • destPos:目标数组的起始位置;
  • length:要复制的元素数量。

示例:删除指定索引的元素(优化版)

public static int[] removeElementByIndexOptimized(int[] arr, int index) {  
    if (index < 0 || index >= arr.length) {  
        throw new IndexOutOfBoundsException();  
    }  
    int[] newArr = new int[arr.length - 1];  
    System.arraycopy(arr, 0, newArr, 0, index);  // 复制前半部分  
    System.arraycopy(arr, index + 1, newArr, index, arr.length - index - 1); // 复制后半部分  
    return newArr;  
}  

与手动复制的对比

方法代码复杂度性能
手动循环较高较慢
System.arraycopy()较低更快

方法三:通过 ArrayList 转换(动态扩展)

利用集合类的灵活性

Java 的 ArrayList 是一个动态数组,支持增删操作。通过将原数组转换为 ArrayList,我们可以利用其 remove() 方法,再转换回数组。

示例:删除指定元素

public static int[] removeElementByValue(int[] arr, int target) {  
    List<Integer> list = new ArrayList<>(arr.length);  
    for (int num : arr) {  
        list.add(num);  
    }  
    list.remove(Integer.valueOf(target));  // 注意:必须用包装类型  
    return list.stream().mapToInt(i -> i).toArray();  
}  

// 使用示例  
int[] original = {5, 6, 7, 8, 7};  
int[] result = removeElementByValue(original, 7);  // 删除第一个7  
System.out.println(Arrays.toString(result));  // 输出:[5, 6, 8, 7]  

注意事项

  • ArrayListremove() 方法默认删除第一个匹配元素;
  • 转换为 ArrayList 需要额外的内存开销;
  • 此方法适用于需要频繁增删元素的场景。

进阶技巧:删除所有匹配元素

多条件删除

如果需要删除数组中所有与目标值相同的元素,可以结合遍历与条件判断:

示例:删除所有指定值的元素

public static int[] removeAllOccurrences(int[] arr, int target) {  
    int count = 0;  
    for (int num : arr) {  
        if (num != target) {  
            count++;  
        }  
    }  
    int[] newArr = new int[count];  
    int index = 0;  
    for (int num : arr) {  
        if (num != target) {  
            newArr[index++] = num;  
        }  
    }  
    return newArr;  
}  

// 使用示例  
int[] original = {2, 4, 6, 4, 8, 4};  
int[] result = removeAllOccurrences(original, 4);  // 删除所有4  
System.out.println(Arrays.toString(result));  // 输出:[2, 6, 8]  

性能分析与选择建议

时间复杂度对比

方法时间复杂度空间复杂度
手动复制法O(n)O(n)
System.arraycopy()O(n)O(n)
ArrayList 转换O(n)O(n)

适用场景总结

场景描述推荐方法
需要删除单个元素且追求简洁System.arraycopy()
需要删除多个元素或复杂逻辑手动循环或 ArrayList
性能要求严格(如大数据量)手动循环(减少对象创建)

实战案例:动态数组的增删操作

综合应用示例

假设我们需要实现一个简单的“购物车”功能,支持添加、删除商品,并通过数组存储数据。

完整代码实现

public class ShoppingCart {  
    private int[] items;  
    private int size;  

    public ShoppingCart(int capacity) {  
        items = new int[capacity];  
        size = 0;  
    }  

    public void addItem(int item) {  
        if (size >= items.length) {  
            // 动态扩容(此处简化处理)  
            items = Arrays.copyOf(items, items.length * 2);  
        }  
        items[size++] = item;  
    }  

    public void removeItem(int index) {  
        if (index < 0 || index >= size) {  
            throw new IndexOutOfBoundsException();  
        }  
        // 复制后半部分覆盖目标索引  
        System.arraycopy(items, index + 1, items, index, size - index - 1);  
        size--;  
    }  

    public void display() {  
        System.out.println(Arrays.toString(Arrays.copyOf(items, size)));  
    }  
}  

// 使用示例  
ShoppingCart cart = new ShoppingCart(3);  
cart.addItem(100); cart.addItem(200); cart.addItem(300);  
cart.display();  // [100, 200, 300]  
cart.removeItem(1);  
cart.display();  // [100, 300, 300] (注意:最后一个元素未被清理,需进一步优化)  

改进点说明

  • 上述代码中,removeItem 方法通过 System.arraycopy() 直接在原数组上操作,避免了创建新数组;
  • 但未清理数组末尾的冗余元素,实际开发中需根据需求选择是否保留或优化。

结论

通过本文的讲解,我们深入探讨了Java 实例 – 删除数组元素的多种实现方式,包括手动复制、System.arraycopy() 以及 ArrayList 的动态转换。每种方法各有优劣,开发者需根据具体场景(如性能要求、代码简洁性等)进行选择。对于初学者,建议从手动循环开始理解原理,再逐步尝试更高效的工具方法。掌握数组删除的核心逻辑后,读者可以将其扩展至其他场景,如动态数组的实现或复杂数据结构的优化。

希望本文能帮助你在 Java 数组操作中游刃有余,为后续学习更高级的数据结构与算法打下坚实基础。

最新发布