«1. Обзор

В этом руководстве мы рассмотрим различные способы сравнения массивов в Java. Мы рассмотрим обычные методы, а также увидим несколько примеров с использованием лямбда-выражений.

2. Сравнение массивов

Мы будем сравнивать массивы в Java, а, как мы знаем, это объекты. Поэтому давайте освежим некоторые основные понятия:

    Объекты имеют ссылки и значения Две одинаковые ссылки должны указывать на одно и то же значение Два разных значения должны иметь разные ссылки Два одинаковых значения не обязательно имеют одинаковые ссылки Примитивные значения сравниваются только по значению Строковые литералы сравниваются только по значению

2.1. Сравнение ссылок на объекты

Если у нас есть две ссылки, указывающие на один и тот же массив, мы всегда должны получать результат true при сравнении на равенство с оператором ==.

Давайте рассмотрим пример:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = planes1;

Сначала мы создали массив моделей самолетов, на которые ссылается planes1. Затем мы создаем planes2, который ссылается на planes1. Делая это, мы создаем две ссылки на один и тот же массив в памяти. Следовательно, выражение «planes1 == planes2» вернет true.

Для массивов метод equals() аналогичен оператору ==. Итак, planes1.equals(planes2) возвращает true, потому что обе ссылки ссылаются на один и тот же объект. Вообще говоря, array1.eqauls(array2) возвращает значение true тогда и только тогда, когда выражение «array1 == array2» возвращает значение true.

Давайте проверим, совпадают ли две ссылки:

assertThat(planes1).isSameAs(planes2);

Теперь давайте убедимся, что значения, на которые ссылается planes1, на самом деле совпадают со значениями, на которые ссылается planes2. Следовательно, мы можем изменить массив, на который ссылается planes2, и проверить, повлияют ли эти изменения на массив, на который ссылается planes1:

planes2[0] = "747";

Чтобы наконец увидеть, как это работает, давайте сделаем наши утверждения:

assertThat(planes1).isSameAs(planes2);
assertThat(planes2[0]).isEqualTo("747");
assertThat(planes1[0]).isEqualTo("747");

С В этом модульном тесте мы смогли сравнить два массива по ссылке.

Однако мы только доказали, что одна ссылка, однажды присвоенная значению другой, будет ссылаться на то же самое значение.

Теперь мы создадим два разных массива с одинаковыми значениями:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };

Так как это разные объекты, мы точно знаем, что они не одинаковы. Таким образом, мы можем их сравнить:

assertThat(planes1).isNotSameAs(planes2);

Подводя итог, в этом случае у нас есть два массива в памяти, которые содержат одни и те же значения String в точно таком же порядке. Однако различаются не только массивы, на которые ссылаются, по содержанию, но и сами ссылки.

2.2. Сравнение длин массивов

Длину массивов можно сравнивать независимо от типов их элементов и от того, заполнены ли их значения.

Давайте создадим два массива:

final String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
final Integer[] quantities = new Integer[] { 10, 12, 34, 45, 12, 43, 5, 2 };

Это два разных массива с различные типы элементов. В этом наборе данных мы регистрируем, например, сколько самолетов каждой модели хранится на складе. Давайте теперь запустим для них модульные тесты:

assertThat(planes1).hasSize(8);
assertThat(quantities).hasSize(8);

Таким образом, мы доказали, что оба массива имеют восемь элементов и что свойство length возвращает правильное количество элементов для каждого массива.

2.3. Сравнение массивов с Arrays.equals

До сих пор мы сравнивали массивы только на основе идентификаторов их объектов. С другой стороны, чтобы проверить, равны ли два массива с точки зрения их содержимого, Java предоставляет статический метод Arrays.equals. Этот метод будет параллельно перебирать массивы для каждой позиции и применять оператор == для каждой пары элементов.

Давайте создадим два разных массива с одинаковыми строковыми литералами в точно таком же порядке:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };

А теперь давайте утверждаем, что они равны:

assertThat(Arrays.equals(planes1, planes2)).isTrue();

Если мы изменим порядок значений второго массива:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = new String[] { "B738", "A320", "A321", "A319", "B77W", "B737", "A333", "A332" };

Получим другой результат:

assertThat(Arrays.equals(planes1, planes2)).isFalse();

2.4. Сравнение массивов с Arrays.deepEquals

Использовать оператор == легко, если мы используем простые типы в Java. Это могут быть примитивные типы или строковые литералы. Сравнение между массивами объектов может быть более сложным. Причина этого полностью объясняется в нашей статье Arrays.deepEquals. Давайте посмотрим пример.

Во-первых, давайте начнем с класса Plane:

public class Plane {
    private final String name;
    private final String model;

    // getters and setters
}

«

@Override
public boolean equals(Object o) {
    if (this == o)
        return true;
    if (o == null || getClass() != o.getClass())
        return false;
    Plane plane = (Plane) o;
    return Objects.equals(name, plane.name) && Objects.equals(model, plane.model);
}

@Override
public int hashCode() {
    return Objects.hash(name, model);
}

«И давайте реализуем методы hashCode и equals:

Plane[][] planes1 
  = new Plane[][] { new Plane[]{new Plane("Plane 1", "A320")}, new Plane[]{new Plane("Plane 2", "B738") }};
Plane[][] planes2 
  = new Plane[][] { new Plane[]{new Plane("Plane 1", "A320")}, new Plane[]{new Plane("Plane 2", "B738") }};

Во-вторых, давайте создадим следующие двухэлементные массивы:

assertThat(Arrays.deepEquals(planes1, planes2)).isTrue();

Теперь посмотрим, являются ли они истинными, глубоко равными массивами:

Plane[][] planes1 
  = new Plane[][] { new Plane[]{new Plane("Plane 1", "A320")}, new Plane[]{new Plane("Plane 2", "B738") }};
Plane[][] planes2 
  = new Plane[][] { new Plane[]{new Plane("Plane 2", "B738")}, new Plane[]{new Plane("Plane 1", "A320") }};

~~ ~ Чтобы убедиться, что наше сравнение работает должным образом, давайте теперь изменим порядок нашего последнего массива:

assertThat(Arrays.deepEquals(planes1, planes2)).isFalse();

Наконец, давайте проверим, действительно ли они больше не равны:

2.5. Сравнение массивов с разным порядком элементов

Comparator<Plane> planeComparator = (o1, o2) -> {
    if (o1.getName().equals(o2.getName())) {
        return o2.getModel().compareTo(o1.getModel());
    }
    return o2.getName().compareTo(o1.getName());
};

Чтобы проверить, равны ли массивы, независимо от порядка элементов, нам нужно определить, что делает один экземпляр нашей плоскости уникальным. В нашем случае достаточно другого имени или модели, чтобы определить, что один самолет отличается от другого. Мы установили это, уже внедрив методы hashCode и equals. Это означает, что прежде чем мы сможем сравнить наши массивы, мы должны их отсортировать. Для этого нам нужен компаратор:

В этом компараторе мы отдаем приоритет имени. Если имена равны, мы разрешаем неоднозначность, глядя на модель. Мы сравниваем строки, используя метод compareTo типа String.

Arrays.sort(planes1[0], planeComparator);
Arrays.sort(planes2[0], planeComparator);

Мы хотим иметь возможность определять, равны ли массивы независимо от порядка сортировки. Для этого давайте теперь отсортируем наши массивы:

assertThat(Arrays.deepEquals(planes1, planes2)).isTrue();

И, наконец, давайте проверим их:

Сначала отсортировав массивы в том же порядке, мы позволим методу deepEquals определить, являются ли эти два массива равны.

3. Заключение

В этом уроке мы рассмотрели разные способы сравнения массивов. Во-вторых, мы увидели разницу между сравнением ссылок и значений. Кроме того, мы рассмотрели, как мы можем глубоко сравнивать массивы. Наконец, мы увидели разницу между обычным сравнением и глубоким сравнением с использованием equals и deepEquals соответственно.