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