Хотя каждый, естественно, согласился бы со следующими строками кода на основании ссылочного равенства и равенства значений и того, что String и обертки переопределяют equalsметод, сначала требуется некоторое усилие, чтобы принять поведение Arrays.equalsиArrays.deepEquals
Object obj1=new Object();
Object obj2=new Object();
String hello1=new String("hello");
String hello2=new String("hello");
System.out.println(hello1.equals(hello2)); //returns true
System.out.println(hello1==hello2); //returns false
System.out.println(obj1.equals(obj2)); //returns false
System.out.println(obj1==obj2); //returns false
Скучный материал ?
Во-первых, сходство
И то, Arrays.equalsи другое Arrays.deepEqualsпохоже на определенные виды поведения, как
- Если это один и тот же объект (ссылочное равенство), они возвращают true
- Если какой-либо из сравниваемых объектов имеет значение null, вернуть false
- Если длины массива не равны, вернуть false
- Они заботятся о порядке (позиции)
Далее различия
Arrays.equals действительно просто кожа глубоко
Открыв соус, мы увидели, что паршивец Array.equalsделает это
for (int i=0; i<length; i++) {
Object o1 = a[i];
Object o2 = a2[i];
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
Таким образом, он просто перебирает заданные массивы, делает a equalsна каждой из пар. Это означает, что если вы передаете Stringмассив / Wrapper или любые другие массивы, у которых метод equals переопределен, то они равны. Переопределенные не равные классы (производные от Object) вернут false.
Arrays.deepEquals выглядит действительно глубоко
Из источника мы могли понять, что Arrays.deepEquals
- Перебирает входные массивы, получает каждую пару
- Анализирует тип каждой пары
- Делегирует
equalрешающую логику одному из перегруженных,Arrays.equalsесли они являются одним из примитивных массивов. - Рекурсивно делегирует,
Arrays.deepEqualsесли это массив объектов - Вызывает соответствующий объект
equalsдля любого другого объектаfor (int i = 0; i < length; i++) { Object e1 = a1[i]; Object e2 = a2[i]; if (e1 == e2) continue; if (e1 == null) return false; // Figure out whether the two elements are equal boolean eq; if (e1 instanceof Object[] && e2 instanceof Object[]) eq = deepEquals ((Object[]) e1, (Object[]) e2); else if (e1 instanceof byte[] && e2 instanceof byte[]) eq = equals((byte[]) e1, (byte[]) e2); … … else eq = e1.equals(e2); if (!eq) return false; } return true;
Теперь классные вещи!
Равен не переопределен (вложенный и не вложенный)
Выполняя Arrays.equals для вложенных или не вложенных объектов «не переопределенные равно», можно с уверенностью предположить, что если Arrays.equals возвращает false, то Arrays.deepEquals также возвращает false.
Невложенных
Итак, с учетом двух массивов
private YourClass[] equalsNotOverriddenArrayNonNested1={new YourClass(), new YourClass()};
private YourClass[] equalsNotOverriddenArrayNonNested2={new YourClass(), new YourClass()};
где YourClassпросто
public class YourClass {
}
тогда верны следующие утверждения
assertFalse(Arrays.equals(equalsNotOverriddenArrayNonNested1, equalsNotOverriddenArrayNonNested2)); assertFalse(Arrays.deepEquals(equalsNotOverriddenArrayNonNested1, equalsNotOverriddenArrayNonNested2));
Уплотненный
Также дано два массива,
private Object[] equalsNotOverriddenArrayNested1={new YourClass(), new YourClass[]{new YourClass()}};
private Object[] equalsNotOverriddenArrayNested2={new YourClass(), new YourClass()};
следующие утверждения верны
assertFalse(Arrays.equals(equalsNotOverriddenArrayNested1, equalsNotOverriddenArrayNested2)); assertFalse(Arrays.deepEquals(equalsNotOverriddenArrayNested1, equalsNotOverriddenArrayNested2));
Равно переопределено
Невложенных
Выполняя Arrays.equals для не вложенных объектов «переопределенных равных», можно сказать, что если Arrays.equals имеет значение true, то Arrays.deepEquals также возвращает значение true.
Дано два строковых массива
assertTrue(Arrays.equals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2)); assertTrue(Arrays.deepEquals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));
следующие утверждения верны
assertTrue(Arrays.equals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2)); assertTrue(Arrays.deepEquals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));
так как они просто один-на-один equalsв каждой паре.
Уплотненный
Интересный сценарий: при выполнении Arrays.equals для вложенных «переопределенных равных» объектов, если Arrays.equals имеет значение false, то Arrays.deepEquals не должен быть ложным.
Рассмотрим два массива Object, которые имеют два значения — массив String и String
assertFalse(Arrays.equals(equalsOverriddenArrayNested1, equalsOverriddenArrayNested2)); assertTrue(Arrays.deepEquals(equalsOverriddenArrayNested1, equalsOverriddenArrayNested2));
Результат для Arrays.deepEqualsлогичен, поскольку каждый (из источника) метод проходит по каждой паре элементов, проверяет, является ли он типом массива, и вызывает deepEqualsкаждую из этих пар. Если это не тип массива, то он просто вызывает equalsобъект on.
Однако результат Arrays.equalsсложен, но в то же время очевиден. Arrays.equalsМетод вслепую вызывает equalsна каждой паре , и поскольку вторые аргументы String[]имеют Objectтип (которого equalsне переопределяется), он проверяет равенство ссылок и не !!
Весь тестовый пример можно найти ниже:
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.util.Arrays;
import org.testng.annotations.Test;
public class DeepEquals {
private Object[] equalsOverriddenArrayNonNested1={"101","201"};
private Object[] equalsOverriddenArrayNonNested2={"101","201"};
private YourClass[] equalsNotOverriddenArrayNonNested1={new YourClass(), new YourClass()};
private YourClass[] equalsNotOverriddenArrayNonNested2={new YourClass(), new YourClass()};
private Object[] equalsNotOverriddenArrayNested1={new YourClass(), new YourClass[]{new YourClass()}};
private Object[] equalsNotOverriddenArrayNested2={new YourClass(), new YourClass()};
private Object[] equalsOverriddenArrayNested1={new String("hello"), new String[]{new String("hello")}};
private Object[] equalsOverriddenArrayNested2={new String("hello"), new String[]{new String("hello")}};
@Test
public void stringArrayTest(){
assertFalse (equalsOverriddenArrayNonNested1==equalsOverriddenArrayNonNested2);
assertFalse(equalsOverriddenArrayNonNested1.equals(equalsOverriddenArrayNonNested2));
assertTrue(Arrays.equals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));
assertTrue(Arrays.deepEquals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));
}
@Test
public void objectArrayTestNonNested(){
assertFalse (equalsNotOverriddenArrayNonNested1==equalsNotOverriddenArrayNonNested2);
assertFalse(equalsNotOverriddenArrayNonNested1.equals(equalsNotOverriddenArrayNonNested2));
assertFalse(Arrays.equals(equalsNotOverriddenArrayNonNested1, equalsNotOverriddenArrayNonNested2));
assertFalse(Arrays.deepEquals(equalsNotOverriddenArrayNonNested1, equalsNotOverriddenArrayNonNested2));
}
@Test
public void objectArrayTestNested(){
assertFalse (equalsNotOverriddenArrayNested1==equalsNotOverriddenArrayNested2);
assertFalse(equalsNotOverriddenArrayNested1.equals(equalsNotOverriddenArrayNested2));
assertFalse(Arrays.equals(equalsNotOverriddenArrayNested1, equalsNotOverriddenArrayNested2));
assertFalse(Arrays.deepEquals(equalsNotOverriddenArrayNested1, equalsNotOverriddenArrayNested2));
}
@Test
public void objectArrayTest2(){
assertFalse (equalsOverriddenArrayNested1==equalsOverriddenArrayNested2);
assertFalse(equalsOverriddenArrayNested1.equals(equalsOverriddenArrayNested2));
assertFalse(Arrays.equals(equalsOverriddenArrayNested1, equalsOverriddenArrayNested2));
assertTrue(Arrays.deepEquals(equalsOverriddenArrayNested1, equalsOverriddenArrayNested2));
}
}