Перейти к основному содержимому

Сравнение массивов в Java

· 7 мин. чтения

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 . Это могут быть примитивные типы или строковые литералы. Сравнение между массивами Object может быть более сложным. Причина этого полностью объясняется в нашей статье Arrays.deepEquals . Давайте посмотрим пример.

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

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

// getters and setters
}

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

@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);
}

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

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. Сравнение массивов с разным порядком элементов

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

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

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

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

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

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

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

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

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

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

Как всегда, полный исходный код примеров доступен на GitHub.