Хотя каждый, естественно, согласился бы со следующими строками кода на основании ссылочного равенства и равенства значений и того, что 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)); } }