1. 概述

在这个教程中,我们将了解Arrays类中的mismatch()方法的行为。这个方法有三个主要重载版本,适用于不同类型的数组。我们将以int数组为例进行讲解。

2. 基础mismatch()方法

2.1. 共同前缀的长度

mismatch()方法接受两个数组,并返回它们之间第一个不同的元素的索引。例如,{1, 2, 3, 4, 5}{1, 2, 3, 5, 8}在索引3处不同。

让我们使用JUnit5编写一个单元测试来验证方法是否按预期工作:

@Test
void givenTwoArraysWithACommonPrefix_whenMismatch_thenIndexOfFirstMismatch() {
    int[] firstArray = {1, 2, 3, 4, 5};
    int[] secondArray = {1, 2, 3, 5, 8};
    assertEquals(3, Arrays.mismatch(firstArray, secondArray));
}

我们可以注意到,如果一个数组是另一个数组的前缀,结果将是较短数组的长度:

@Test
void givenFirstArrayIsAPrefixOfTheSecond_whenMismatch_thenFirstArrayLength() {
    int[] firstArray = {1, 2, 3, 4, 5};
    int[] secondArray = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    assertEquals(firstArray.length, Arrays.mismatch(firstArray, secondArray));
}

如果两个数组的第一个元素不同,结果为0:

@Test
void givenNoCommonPrefix_whenMismatch_thenZero() {
    int[] firstArray = {1, 2, 3, 4, 5};
    int[] secondArray = {9, 8, 7};
    assertEquals(0, Arrays.mismatch(firstArray, secondArray));
}

2.2. 边界情况

当两个数组具有相同的元素且顺序相同时,方法返回-1:

@Test
void givenTwoEmptyArrays_whenMismatch_thenMinusOne() {
    assertEquals(-1, Arrays.mismatch(new int[] {}, new int[] {}));
}

请注意,两个空数组没有差异,因此在这种情况下结果也是-1:

@Test
void givenTwoEmptyArrays_whenMismatch_thenMinusOne() {
    assertEquals(-1, Arrays.mismatch(new int[] {}, new int[] {}));
}

2.3. 空或空数组

然而,如果两个数组中恰好有一个为空,mismatch()方法将返回空数组的长度,即0:

@Test
void givenExactlyOneAnEmptyArray_whenMismatch_thenZero() {
    int[] firstArray = {};
    int[] secondArray = {1, 2, 3};
    assertEquals(0, Arrays.mismatch(firstArray, secondArray));
}

最后但同样重要的是,如果任何一个数组为nullmismatch()方法会抛出NullPointerException

@Test
void givenAtLeastANullArray_whenMismatch_thenThrowsNullPointerException() {
    int[] firstArray = null;
    int[] secondArray = {1, 2, 3, 4, 5};
    assertThrows(NullPointerException.class, () -> Arrays.mismatch(firstArray, secondArray));
}

此外,我们还可以将mismatch()方法应用于booleanbytecharshortintlongfloatdoubleObject数组。

3. 使用子数组的mismatch()方法

接下来,我们将关注一个签名在int数组中的方法变体:int mismatch(int[] a, int aFromIndex, int aToIndex, int[] b, int bFromIndex, int bToIndex)这个方法版本会从每个数组中提取子数组,然后检查差异。

3.1. 子数组的相似行为

让我们通过一个例子来看它的行为:

@Test
void givenTwoSubArraysWithACommonPrefix_whenMismatch_thenIndexOfFirstMismatch() {
    int[] firstArray = {1, 2, 3, 4, 5};
    int[] secondArray = {0, 1, 2, 3, 5, 8};
    assertEquals(3, Arrays.mismatch(firstArray, 0, 4, secondArray, 1, 6));
}

逐步理解发生了什么:

  • 首先,方法计算原始第一个数组{1, 2, 3, 4, 5}从索引0到4的子数组:结果是新数组{1, 2, 3, 4}
  • 然后,它计算原始第二个数组{0, 1, 2, 3, 5, 8}从索引1到6的子数组:结果是数组{1, 2, 3, 5, 8}
  • 最后,它对两个子数组应用基础的mismatch()方法:{1, 2, 3, 4}{1, 2, 3, 5, 8};两者都以{1, 2, 3}的顺序开始,但在第四个元素处存在差异

因此,在这种情况下,方法返回4。与基础版本相同,所有点都适用于这些变体:

  • 如果没有差异,方法返回-1
  • 如果任何数组为null,方法会抛出NullPointerException
  • 这个方法也适用于booleanbytecharshortintlongfloatdoubleObject数组

3.2. 其他异常

除了标准行为外,子数组方法还会引入新的异常。如果对于任意数组,"from"索引大于"to"索引,mismatch()方法会抛出IllegalArgumentException

@Test
void givenToIndexGreaterThanFromIndex_whenMismatch_thenThrowsIllegalArgumentException() {
    int[] firstArray = {2, 3, 4, 5, 4, 3, 2};
    int[] secondArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    assertThrows(IllegalArgumentException.class, () -> Arrays.mismatch(firstArray, 4, 2, secondArray, 0, 6));
}

此外,如果我们传递非法索引作为参数,方法会抛出ArrayIndexOutOfBoundsException

@Test
void givenIllegalIndexes_whenMismatch_thenThrowsArrayIndexOutOfBoundsException() {
    int[] firstArray = {2, 3, 4, 5, 4, 3, 2};
    int[] secondArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    assertThrows(ArrayIndexOutOfBoundsException.class, () -> Arrays.mismatch(firstArray, -1, 2, secondArray, 0, 6));
}

总之,"from"索引必须大于0,而"to"索引必须大于"from"索引且小于数组的长度。

4. 使用比较器的泛型方法

最后一个方法版本接受两个泛型类型的数组,并根据一个Comparator计算第一个不匹配项。例如,我们可以拿两个String数组并使用String类的CASE_INSENSITIVE_ORDER比较器:

@Test
void givenTwoStringArraysAndAComparator_whenMismatch_thenIndexOfFirstMismatch() {
    String[] firstArray = {"one", "two", "three"};
    String[] secondArray = {"ONE", "TWO", "FOUR"};
    Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
    assertEquals(2, Arrays.mismatch(firstArray, secondArray, comparator));
}

关于边界情况和异常的所有先前章节中的点仍然适用。此外,如果提供的Comparatornull,也会抛出NullPointerException

@Test
void givenAtLeastANullArrayOrNullComparator_whenMismatch_thenThrowsNullPointerException() {
    String[] firstArray = {"one"};
    String[] secondArray = {"one"};
    Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
    assertThrows(NullPointerException.class, () -> Arrays.mismatch(firstArray, secondArray, null));
}

最后,我们还可以注意到,有一个类似的子数组方法重写:

@Test
void givenTwoStringSubarraysAndAComparator_whenMismatch_thenIndexOfFirstMismatch() {
    String[] firstArray = {"one", "two", "three", "four"};
    String[] secondArray = {"ZERO", "ONE", "TWO", "FOUR", "EIGHT", "SIXTEEN"};
    Comparator<String> comparator = String.CASE_INSENSITIVE_ORDER;
    assertEquals(2, Arrays.mismatch(firstArray, 0, 4, secondArray, 1, 3, comparator));
}

5. 总结

在这篇文章中,我们计算了两个数组之间的第一个不匹配。Java 9为此引入了mismatch()方法。我们看到这个方法在很多情况下非常方便,因为它提供了大量的重写选项。

如往常一样,代码可以在GitHub上找到。