合併Java中的兩個數組

1.概述

在本教程中,我們將討論如何在Java中連接兩個數組。

首先,我們將使用標準Java API實現我們自己的方法。

然後,我們將看看如何使用常用庫解決問題。

2.問題簡介

簡單的例子可以清楚地說明問題。

假設我們有兩個數組:

String[] strArray1 = {"element 1", "element 2", "element 3"};

 String[] strArray2 = {"element 4", "element 5"};

現在,我們想加入他們並獲得一個新的數組:

String[] expectedStringArray = {"element 1", "element 2", "element 3", "element 4", "element 5"}

另外,我們不希望我們的方法僅適用於String數組,因此我們將尋找通用解決方案

而且,我們不應該忘記原始數組的情況。如果我們的解決方案也適用於原始數組,那就太好了:

int[] intArray1 = { 0, 1, 2, 3 };

 int[] intArray2 = { 4, 5, 6, 7 };

 int[] expectedIntArray = { 0, 1, 2, 3, 4, 5, 6, 7 };

在本教程中,我們將介紹解決問題的不同方法。

3.使用Java Collections

當我們研究這個問題時,可能會找到一個快速的解決方案。

好吧,Java沒有提供連接數組的輔助方法。但是,從Java 5開始, Collections實用程序類引入了addAll(Collection<? super T> c, T… elements)方法。

我們可以創建一個List對象,然後調用此方法兩次,以將兩個數組添加到列表中。最後,我們將結果List轉換回數組:

static <T> T[] concatWithCollection(T[] array1, T[] array2) {

 List<T> resultList = new ArrayList<>(array1.length + array2.length);

 Collections.addAll(resultList, array1);

 Collections.addAll(resultList, array2);



 @SuppressWarnings("unchecked")

 //the type cast is safe as the array1 has the type T[]

 T[] resultArray = (T[]) Array.newInstance(array1.getClass().getComponentType(), 0);

 return resultList.toArray(resultArray);

 }

在上面的方法中,我們使用Java反射API創建通用數組實例: resultArray.

讓我們編寫一個測試來驗證我們的方法是否有效:

@Test

 public void givenTwoStringArrays_whenConcatWithList_thenGetExpectedResult() {

 String[] result = ArrayConcatUtil.concatWithCollection(strArray1, strArray2);

 assertThat(result).isEqualTo(expectedStringArray);

 }

如果我們執行測試,它將通過。

這種方法非常簡單。但是,由於該方法接受T[]數組,因此它不支持串聯基本數組

除此之外,它在創建ArrayList對象時效率很低,稍後我們調用toArray()方法將其轉換回array 。在此過程中,Java List對象增加了不必要的開銷。

接下來,讓我們看看是否可以找到一種更有效的方法來解決問題。

4.使用陣列複製技術

Java沒有提供數組連接方法,但是提供了兩種數組複製方法: System.arraycopy()Arrays.copyOf()

我們可以使用Java的數組複製方法解決問題。

這個想法是,我們創建一個新數組,即result ,其中有resultlength = array1.length + array2.length ,然後將每個數組的元素複製到result數組。

4.1 非原始數組

首先,讓我們看一下方法的實現:

static <T> T[] concatWithArrayCopy(T[] array1, T[] array2) {

 T[] result = Arrays.copyOf(array1, array1.length + array2.length);

 System.arraycopy(array2, 0, result, array1.length, array2.length);

 return result;

 }

該方法看起來很緊湊。此外,整個方法僅創建了一個新的數組對象: result

現在,讓我們編寫一個測試方法來檢查它是否按預期工作:

@Test

 public void givenTwoStringArrays_whenConcatWithCopy_thenGetExpectedResult() {

 String[] result = ArrayConcatUtil.concatWithArrayCopy(strArray1, strArray2);

 assertThat(result).isEqualTo(expectedStringArray);

 }

如果我們進行測試,則測試將通過。

沒有不必要的對象創建。因此,此方法比使用Java Collections .

另一方面,此通用方法僅接受T[]類型的參數。因此,我們無法將原始數組傳遞給該方法。

但是,我們可以修改該方法以使其支持基本數組。

接下來,讓我們仔細看看如何添加基本數組支持。

4.2 添加基本數組支持

為了使該方法支持基本數組,我們需要將參數的類型從T[]更改為T並進行一些類型安全的檢查。

首先,讓我們看一下修改後的方法:

static <T> T concatWithCopy2(T array1, T array2) {

 if (!array1.getClass().isArray() || !array2.getClass().isArray()) {

 throw new IllegalArgumentException("Only arrays are accepted.");

 }



 Class<?> compType1 = array1.getClass().getComponentType();

 Class<?> compType2 = array2.getClass().getComponentType();



 if (!compType1.equals(compType2)) {

 throw new IllegalArgumentException("Two arrays have different types.");

 }



 int len1 = Array.getLength(array1);

 int len2 = Array.getLength(array2);



 @SuppressWarnings("unchecked")

 //the cast is safe due to the previous checks

 T result = (T) Array.newInstance(compType1, len1 + len2);



 System.arraycopy(array1, 0, result, 0, len1);

 System.arraycopy(array2, 0, result, len1, len2);



 return result;

 }

顯然, concatWithCopy2()方法比原始版本更長。但這並不難理解。現在,讓我們快速遍歷它以了解其工作原理。

由於該方法現在允許使用類型T參數,因此我們需要確保兩個參數都是數組

if (!array1.getClass().isArray() || !array2.getClass().isArray()) {

 throw new IllegalArgumentException("Only arrays are accepted.");

 }

如果兩個參數是數組,仍然不夠安全。例如,我們不想連接Integer[]數組和String[]數組。因此,我們需要**確保兩個數組ComponentType**相同:

if (!compType1.equals(compType2)) {

 throw new IllegalArgumentException("Two arrays have different types.");

 }

在進行類型安全檢查之後,我們可以使用ConponentType對象創建通用數組實例,並將參數數組複製到result數組。它與以前的concatWithCopy()方法非常相似。

4.3 測試concatWithCopy2()方法

接下來,讓我們測試一下我們的新方法是否按預期工作。首先,我們傳遞兩個非數組對象,看看該方法是否引發預期的異常:

@Test

 public void givenTwoStrings_whenConcatWithCopy2_thenGetException() {

 String exMsg = "Only arrays are accepted.";

 try {

 ArrayConcatUtil.concatWithCopy2("String Nr. 1", "String Nr. 2");

 fail(String.format("IllegalArgumentException with message:'%s' should be thrown. But it didn't", exMsg));

 } catch (IllegalArgumentException e) {

 assertThat(e).hasMessage(exMsg);

 }

 }

在上面的測試中,我們將兩個String對像傳遞給該方法。如果我們執行測試,則測試通過。這意味著我們已經有了預期的例外。

最後,讓我們構建一個測試來檢查新方法是否可以連接基本數組:

@Test

 public void givenTwoArrays_whenConcatWithCopy2_thenGetExpectedResult() {

 String[] result = ArrayConcatUtil.concatWithCopy2(strArray1, strArray2);

 assertThat(result).isEqualTo(expectedStringArray);



 int[] intResult = ArrayConcatUtil.concatWithCopy2(intArray1, intArray2);

 assertThat(intResult).isEqualTo(expectedIntArray);

 }

這次,我們concatWithCopy2()方法。首先,我們傳遞兩個String[]數組。然後,我們傳遞兩個int[]基本數組。

如果我們運行測試,則該測試將通過。現在,我們可以說, concatWithCopy2()方法可以按預期工作。

5.使用Java Stream API

如果我們使用的Java版本是8或更高版本,則可以使用Stream API。我們還可以使用Stream API解決問題。

首先,我們可以Arrays.stream()方法從數組中Stream另外, Stream類提供了一個靜態的concat()方法來連接兩個Stream對象。

現在,讓我們看看如何用Stream.

5.1 級聯非原始數組

使用Java Streams構建通用解決方案非常簡單:

static <T> T[] concatWithStream(T[] array1, T[] array2) {

 return Stream.concat(Arrays.stream(array1), Arrays.stream(array2))

 .toArray(size -> (T[]) Array.newInstance(array1.getClass().getComponentType(), size));

 }

首先,我們將兩個輸入數組轉換為Stream對象。其次,我們使用Stream.concat()方法Stream

最後,我們返回一個包含級聯Stream.

接下來,讓我們構建一個簡單的測試方法來檢查解決方案是否有效:

@Test

 public void givenTwoStringArrays_whenConcatWithStream_thenGetExpectedResult() {

 String[] result = ArrayConcatUtil.concatWithStream(strArray1, strArray2);

 assertThat(result).isEqualTo(expectedStringArray);

 }

如果我們通過兩個String[]數組,則測試將通過。

可能已經註意到,我們的通用方法接受T[]類型的參數。因此,它不適用於基本數組

接下來,讓我們看看如何使用Java Streams連接兩個基本數組。

5.2 級聯原始數組

Stream API附帶了不同的Stream類,這些類可以將Stream對象轉換為相應的原始數組,例如IntStream, [LongStream ,](https://docs.oracle.com/javase/8/docs/api/java/util/stream/LongStream.html)DoubleStream

但是,只有intlongdouble具有其Stream類型。也就是說,如果要連接的基本數組的類型為int[]long[]double[] ,則可以選擇正確的Stream類並調用concat()方法。

讓我們看一個IntStream int[]數組的示例:

static int[] concatIntArraysWithIntStream(int[] array1, int[] array2) {

 return IntStream.concat(Arrays.stream(array1), Arrays.stream(array2)).toArray();

 }

如上面的方法所示, Arrays.stream(int[])方法將返回一個IntStream對象。

同樣, IntStream.toArray()方法返回int[] 。因此,我們不需要照顧類型轉換。

像往常一樣,讓我們創建一個測試,看看它是否可以與我們的int[]輸入數據一起使用:

@Test

 public void givenTwoIntArrays_whenConcatWithIntStream_thenGetExpectedResult() {

 int[] intResult = ArrayConcatUtil.concatIntArraysWithIntStream(intArray1, intArray2);

 assertThat(intResult).isEqualTo(expectedIntArray);

 }

如果我們運行測試,它將通過。

6.使用Apache Commons Lang庫

Apache Commons Lang庫在現實世界中廣泛用於Java應用程序中。

它帶有ArrayUtils類,其中包含許多方便的數組幫助器方法。

ArrayUtils類提供了一系列addAll()方法,這些方法支持連接非原始數組和原始數組。

讓我們通過一種測試方法來驗證它:

@Test

 public void givenTwoArrays_whenConcatWithCommonsLang_thenGetExpectedResult() {

 String[] result = ArrayUtils.addAll(strArray1, strArray2);

 assertThat(result).isEqualTo(expectedStringArray);



 int[] intResult = ArrayUtils.addAll(intArray1, intArray2);

 assertThat(intResult).isEqualTo(expectedIntArray);

 }

在內部, ArrayUtils.addAll()方法使用高性能的System.arraycopy()方法進行數組連接。

7.使用Guava庫

與Apache Commons庫類似,Guava是另一個受到許多開發人員喜愛的庫。

Guava還提供了方便的幫助程序類來進行數組串聯。

如果要連接非基本數組,則ObjectArrays.concat()方法是一個不錯的選擇:

@Test

 public void givenTwoStringArrays_whenConcatWithGuava_thenGetExpectedResult() {

 String[] result = ObjectArrays.concat(strArray1, strArray2, String.class);

 assertThat(result).isEqualTo(expectedStringArray);

 }

番石榴提供了每個原始的原始實用程序。所有原始實用程序提供 一個concat()方法,用於將數組與相應的類型連接起來,例如:

  • int[] –番石榴: Ints.concat(int[] … arrays)
  • long[] –番石榴: Longs.concat(long[] … arrays)
  • byte[] –番石榴: Bytes.concat(byte[] … arrays)
  • double[] –番石榴: Doubles.concat(double[] … arrays)

我們可以選擇正確的原始實用程序類來連接原始數組。

接下來,讓我們使用Ints.concat()方法int[]

@Test

 public void givenTwoIntArrays_whenConcatWithGuava_thenGetExpectedResult() {

 int[] intResult = Ints.concat(intArray1, intArray2);

 assertThat(intResult).isEqualTo(expectedIntArray);

 }

同樣,Guava System.arraycopy()進行數組連接以獲得良好的性能。