0

0

如何在Java中合并两个数组 Java数组合并方法总结

雪夜

雪夜

发布时间:2025-07-22 18:21:01

|

957人浏览过

|

来源于php中文网

原创

在java中合并两个数组的核心思路是创建新数组并复制元素,常用方法包括:1. 使用system.arraycopy(),高效但需手动计算位置;2. 使用java 8 stream api,代码简洁但性能略低;3. 使用arraylist作为中间容器,灵活但涉及额外开销;4. 手动循环复制,灵活但冗长。合并不同类型的数组需向上转型或使用object数组,但需注意类型安全。处理大型数组时,system.arraycopy()性能最优,stream和arraylist适用于小规模数据。处理null或边界情况时应检查输入并合理处理,如返回空数组或抛出异常,以确保代码健壮性。

如何在Java中合并两个数组 Java数组合并方法总结

在Java中合并两个数组,核心思路是创建一个新的、足够大的数组,然后将原有的两个数组的元素逐一复制到这个新数组中。具体实现方式有很多种,每种都有其适用场景和优缺点,没有绝对的“最佳”方法,更多是权衡性能、代码可读性和特定需求的结果。

如何在Java中合并两个数组 Java数组合并方法总结

解决方案

在Java中合并数组,我个人常用的方法主要有以下几种,它们各有特色:

1. 使用 System.arraycopy()

这是Java提供的一个原生方法,效率非常高,因为它通常是JVM层面的优化,甚至可能调用底层的C/C++实现。但它的缺点是需要手动计算目标数组的起始位置和长度,相对来说比较“底层”。

立即学习Java免费学习笔记(深入)”;

如何在Java中合并两个数组 Java数组合并方法总结
public  T[] mergeArraysByArrayCopy(T[] array1, T[] array2) {
    if (array1 == null && array2 == null) {
        return null; // 或者抛出异常,视具体业务而定
    }
    if (array1 == null) return array2;
    if (array2 == null) return array1;

    // 创建一个新数组,长度为两个原数组之和
    T[] result = (T[]) java.lang.reflect.Array.newInstance(array1.getClass().getComponentType(), array1.length + array2.length);

    // 复制第一个数组
    System.arraycopy(array1, 0, result, 0, array1.length);
    // 复制第二个数组,从第一个数组的末尾开始
    System.arraycopy(array2, 0, result, array1.length, array2.length);

    return result;
}

// 示例用法:
// String[] arr1 = {"a", "b"};
// String[] arr2 = {"c", "d"};
// String[] merged = mergeArraysByArrayCopy(arr1, arr2); // {"a", "b", "c", "d"}

对于基本类型数组(如int[], double[]),System.arraycopy同样适用,但不能使用泛型,需要为每种基本类型写一个重载方法,或者直接在调用时指定具体类型。

2. 使用 Java 8 Stream API

如果你喜欢函数式编程风格,或者你的项目已经在使用Java 8及更高版本,Stream API提供了一种非常简洁的合并方式。它代码量少,可读性也不错。

如何在Java中合并两个数组 Java数组合并方法总结
import java.util.Arrays;
import java.util.stream.Stream;

public  T[] mergeArraysByStream(T[] array1, T[] array2) {
    if (array1 == null && array2 == null) {
        return null;
    }
    // 使用Stream.ofNullable处理可能为null的数组,避免NPE
    Stream stream1 = array1 != null ? Arrays.stream(array1) : Stream.empty();
    Stream stream2 = array2 != null ? Arrays.stream(array2) : Stream.empty();

    return Stream.concat(stream1, stream2).toArray(size -> (T[]) java.lang.reflect.Array.newInstance(array1 != null ? array1.getClass().getComponentType() : (array2 != null ? array2.getClass().getComponentType() : Object.class), size));
    // 这里的toArray方法参数比较关键,用于指定返回数组的类型
    // 如果array1和array2都为null,上面Object.class会返回Object[]
}

// 示例用法:
// Integer[] arr1 = {1, 2};
// Integer[] arr2 = {3, 4};
// Integer[] merged = mergeArraysByStream(arr1, arr2); // {1, 2, 3, 4}

// 对于基本类型数组,需要使用对应的原始类型Stream,例如IntStream
// int[] intArr1 = {1, 2};
// int[] intArr2 = {3, 4};
// int[] mergedInt = IntStream.concat(Arrays.stream(intArr1), Arrays.stream(intArr2)).toArray();

Stream方式的缺点在于,对于非常大的数组,其性能可能略逊于System.arraycopy,因为涉及到额外的对象创建(Stream对象、中间操作等)和可能的装箱/拆箱操作(如果处理的是基本类型数组)。

3. 使用 ArrayList 作为中间容器

这是一种非常直观且灵活的方法。先将两个数组转换为列表,然后合并列表,最后再将列表转换回数组。这种方法的好处是,如果后续还需要对合并后的数据进行增删改查操作,保持为列表形式会更方便。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public  T[] mergeArraysByList(T[] array1, T[] array2) {
    if (array1 == null && array2 == null) {
        return null;
    }
    List list = new ArrayList<>();
    if (array1 != null) {
        list.addAll(Arrays.asList(array1));
    }
    if (array2 != null) {
        list.addAll(Arrays.asList(array2));
    }

    // 将List转换回数组
    // 需要传入一个与目标类型匹配的空数组作为参数,以便toArray方法知道返回的数组类型
    return list.toArray((T[]) java.lang.reflect.Array.newInstance(
        (array1 != null && array1.length > 0) ? array1.getClass().getComponentType() :
        ((array2 != null && array2.length > 0) ? array2.getClass().getComponentType() : Object.class), 0));
}

// 示例用法:
// Double[] arr1 = {1.1, 2.2};
// Double[] arr2 = {3.3, 4.4};
// Double[] merged = mergeArraysByList(arr1, arr2); // {1.1, 2.2, 3.3, 4.4}

这种方法在性能上通常不如System.arraycopy,因为涉及集合的动态扩容、装箱/拆箱以及额外的对象创建。但它的代码非常易读,并且在需要灵活操作数据时,作为中间步骤非常实用。

奇布塔
奇布塔

基于AI生成技术的一站式有声绘本创作平台

下载

4. 手动循环复制

这是最基础、最“笨”的方法,但它提供了最大的灵活性,比如你可以在复制过程中对元素进行一些额外的处理或过滤。

public  T[] mergeArraysByLoop(T[] array1, T[] array2) {
    if (array1 == null && array2 == null) {
        return null;
    }
    int len1 = (array1 != null) ? array1.length : 0;
    int len2 = (array2 != null) ? array2.length : 0;

    // 确定新数组的类型,避免空数组时类型推断问题
    Class componentType = Object.class;
    if (array1 != null && array1.length > 0) {
        componentType = array1.getClass().getComponentType();
    } else if (array2 != null && array2.length > 0) {
        componentType = array2.getClass().getComponentType();
    }

    T[] result = (T[]) java.lang.reflect.Array.newInstance(componentType, len1 + len2);

    for (int i = 0; i < len1; i++) {
        result[i] = array1[i];
    }
    for (int i = 0; i < len2; i++) {
        result[len1 + i] = array2[i];
    }
    return result;
}

手动循环虽然直观,但代码相对冗长,且性能通常不如System.arraycopy

合并不同类型的Java数组时有哪些注意事项?

在Java中,数组是强类型的。这意味着一个int[]数组只能存放int类型的数据,一个String[]数组只能存放String类型的数据。因此,直接将一个int[]和一个String[]合并成一个单一类型的数组是不可能的。

然而,如果你想合并逻辑上相关的但具体类型不同的数组,你有几种选择:

  1. 向上转型到共同的父类或接口: 如果你有一个Dog[]和一个Cat[],并且DogCat都实现了Animal接口或继承了Animal类,那么你可以将它们合并到一个Animal[]数组中。这是面向对象多态性的体现。
    // 假设Animal是Dog和Cat的父类或接口
    // Animal[] mergedAnimals = Stream.concat(Arrays.stream(dogs), Arrays.stream(cats)).toArray(Animal[]::new);
  2. 使用 Object[] 数组: Object是所有Java类的终极父类。你可以将任何类型的对象数组合并到Object[]数组中。这样做的好处是通用性强,但缺点是失去了类型信息,后续访问元素时可能需要进行类型强制转换,并且需要处理ClassCastException的风险。
    // String[] stringArr = {"hello"};
    // Integer[] intArr = {123};
    // Object[] mergedObjects = Stream.concat(Arrays.stream(stringArr), Arrays.stream(intArr)).toArray();
    // 此时 mergedObjects 包含 String 和 Integer 对象
  3. 避免直接合并,而是处理数据: 很多时候,不同类型的数组可能代表了不同的数据维度。与其强行合并,不如思考是否应该将它们转化为一个包含多种属性的自定义对象列表,或者使用Map等数据结构来关联它们。例如,一个String[]存储姓名,一个int[]存储年龄,与其合并成一个Object[],不如创建一个List,其中Person对象包含姓名和年龄属性。这通常是更符合领域模型的设计。

总结来说,在Java中合并不同类型的数组,通常意味着你要么将它们向上转型到它们的共同父类(通常是Object),要么重新思考你的数据结构设计,看是否有更合适的方式来组织这些异构数据。

在大型应用中,合并数组的性能考量与优化策略是什么?

在处理大型数组时,性能考量变得尤为重要。选择合适的合并策略可以显著影响应用的响应时间和资源消耗。

  1. System.arraycopy() 通常是首选: 对于大规模数组的合并,System.arraycopy()几乎总是最快的选择。它是一个本地方法(native method),由JVM直接调用底层操作系统或硬件的内存复制指令,避免了Java层面的循环和对象创建开销。如果你的瓶颈在于数组复制本身,并且对内存占用有严格要求,这是你的最佳拍档。

  2. Stream API 的开销: 虽然Stream API代码简洁,但在处理大型数组时,其性能可能不如System.arraycopy()。Stream的链式操作会创建中间对象(如Stream实例、Spliterator等),并且对于基本类型,可能涉及装箱/拆箱操作,这些都会带来额外的CPU和内存开销。对于小到中等规模的数组,这种开销可以忽略不计,但对于百万级甚至千万级元素的数组,累积起来就相当可观了。

  3. ArrayList 中间容器的代价: 使用ArrayList作为中间容器,其主要开销在于:

    • 动态扩容: ArrayList在元素数量超出当前容量时会进行扩容,这涉及创建一个更大的新数组并将旧数组的元素复制过去,这个过程本身就是一次数组复制。虽然ArrayList的扩容策略通常是按比例增加,但对于非常大的数据集,累积的复制操作仍然会增加时间。
    • 装箱/拆箱: 如果你合并的是基本类型数组(如int[]),将它们放入ArrayList中会发生装箱操作,即每个int都会被包装成一个Integer对象。这会显著增加内存占用和GC压力。
    • 对象创建: ArrayList本身以及其内部的元素(如果是对象类型)都需要内存分配。
  4. 内存占用与垃圾回收(GC): 无论哪种合并方法,都需要创建一个新的数组来存放合并后的元素,这意味着在合并过程中,内存中会同时存在三个数组(两个源数组和一个目标数组)。如果你的源数组非常大,这可能会导致临时的内存峰值,甚至触发频繁的Full GC,从而影响应用的整体性能和响应性。 优化策略: 尽量避免在短时间内频繁地合并大型数组。如果可能,考虑是否可以通过迭代器或流的方式处理数据,避免一次性将所有数据加载到内存中。

  5. 预估大小: 如果你知道合并后数组的精确大小,在创建新数组或ArrayList时预先分配好容量(例如new ArrayList(array1.length + array2.length)),可以避免ArrayList的多次扩容,从而提升性能。

  6. 并行处理: 对于极大的数组,如果你的业务逻辑允许,可以考虑使用Java 8的并行流(parallelStream())或者ForkJoinPool来并行地处理数组的某些部分,然后再将结果合并。但这会引入线程管理的复杂性,并非所有场景都适用。

总而言之,在大型应用中,性能优化通常意味着要深入了解底层机制。如果数组合并是性能瓶颈,我通常会优先考虑System.arraycopy()。如果代码简洁性更重要且数组规模不大,Stream API或ArrayList会是更好的选择。最关键的是,在做出决策前,通过基准测试(如使用JMH)来验证你的假设。

如何处理合并数组时可能出现的空值(null)或边界情况?

在实际开发中,输入的数组往往不是那么“完美”,可能会遇到null数组、空数组或只包含null元素的数组等边界情况。健壮的代码应该能够优雅地处理这些情况。

  1. 处理 null 输入数组: 如果传入的array1array2本身就是null,不加处理地直接操作(如array1.lengthArrays.stream(array1))会导致NullPointerException处理方式:

    • 返回非空数组: 如果其中一个数组是null,就返回另一个非null的数组。
    • 返回空数组: 如果两个数组都是null,或者都为空,可以返回一个空数组(例如new T[0])而不是null,这通常更符合API设计习惯,可以避免调用者后续的null检查。
    • 抛出 IllegalArgumentException 如果你的业务逻辑规定输入数组不允许为null,那么抛出异常是一种明确的错误处理方式。
    • 示例(以Stream为例):
      // Stream.ofNullable(array) 可以将null数组转换为一个空的Stream,避免NPE
      Stream stream1 = array1 != null ? Arrays.stream(array1) : Stream.empty();
      Stream stream2 = array2 != null ? Arrays.stream(array2) : Stream.empty();
      // ...后续合并操作

      对于System.arraycopy或手动循环,则需要在调用前显式地检查null

      int len1 = (array1 != null) ? array1.length : 0;
      int len2 = (array2 != null) ? array2.length : 0;
      // ...创建新数组
      if (array1 != null) {
          System.arraycopy(array1, 0, result, 0, len1);
      }
      if (array2 != null) {
          System.arraycopy(array2, 0, result, len1, len2);
      }
  2. 处理空数组(array.length == 0): 空数组通常不会导致运行时错误,因为它们的长度为0,循环不会执行,System.arraycopy也会正确地复制0个元素。合并一个空数组和另一个非空数组,结果就是那个非空数组。这是符合预期的行为。 处理方式: 大多数情况下,不需要特殊处理,现有逻辑就能很好地应对。

  3. 处理包含 null 元素的数组: 数组中可能包含null元素,例如String[] arr = {"a", null, "b"};。合并操作本身不会过滤或改变这些null元素,它们会像其他元素一样被复制到新数组中。 处理方式:

    • 默认行为: 如果null元素是允许的,那么无需特殊处理。
    • 过滤 null 元素: 如果你不希望合并后的数组包含null元素,可以在合并过程中进行过滤。Stream API在这里非常方便:
      // Stream.concat(...).filter(

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

844

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

742

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

740

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

400

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

431

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16926

2023.08.03

菜鸟裹裹入口以及教程汇总
菜鸟裹裹入口以及教程汇总

本专题整合了菜鸟裹裹入口地址及教程分享,阅读专题下面的文章了解更多详细内容。

0

2026.01.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.8万人学习

Go 教程
Go 教程

共32课时 | 4.1万人学习

MongoDB 教程
MongoDB 教程

共17课时 | 2.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号