DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Languages
  4. Your's Deeply - Why Arrays.deepEquals When We Have Arrays.equals

Your's Deeply - Why Arrays.deepEquals When We Have Arrays.equals

Arun Manivannan user avatar by
Arun Manivannan
·
Nov. 13, 12 · Interview
Like (0)
Save
Tweet
Share
12.09K Views

Join the DZone community and get the full member experience.

Join For Free

 While everybody would naturally accept the following lines of code on grounds of reference equality and value equality and that String and wrappers override the equals method, it takes some effort at first to accept the behavior of Arrays.equals and 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


Boring Stuff :-(

First, the similarities

Both Arrays.equals and Arrays.deepEquals are similar in certain behaviors as

  1. If they are the same object (reference equality), they return true
  2. If either of the compared objects are null, then return false
  3. If the array lengths are not equal, then return false
  4. They care about order (position)

Next, the differences

Arrays.equals is really just skin deep

Opening up the souce, we could see that the lousy Array.equals just does this

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

So, it just loops through the given arrays, does a equals on each of the pairs. This means that if you are passing in a String/Wrapper array or any other arrays whose equals method is overridden, then they are equal. Non-equals-overridden classes (derived from Object) will return false.

Arrays.deepEquals looks really deep

From the source, we could understand that Arrays.deepEquals

  1. Loops through the input arrays, gets each pair
  2. Analyses the type of each pair
  3. Delegates the equal deciding logic to one of the overloaded Arrays.equals if they are one of the primitive arrays
  4. Delegates recursively to Arrays.deepEquals if it is an Object array
  5. Calls the respective object’s equals, for any other object
    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;
    

Now, the Awesome stuff !!

Equals not overridden (nested and non-nested)

While doing an Arrays.equals for nested or non-nested ‘non-overridden equals’ objects, it is safe to assume that if Arrays.equals return false, then Arrays.deepEquals also return false.

Non-Nested

So, given two arrays

private YourClass[] equalsNotOverriddenArrayNonNested1={new YourClass(), new YourClass()};
private YourClass[] equalsNotOverriddenArrayNonNested2={new YourClass(), new YourClass()};

where YourClass is simply

public class YourClass {
}

then the following assertions are true

assertFalse(Arrays.equals(equalsNotOverriddenArrayNonNested1, equalsNotOverriddenArrayNonNested2));
assertFalse(Arrays.deepEquals(equalsNotOverriddenArrayNonNested1, equalsNotOverriddenArrayNonNested2));

Nested

Also given two arrays,

private Object[] equalsNotOverriddenArrayNested1={new YourClass(), new YourClass[]{new YourClass()}};
private Object[] equalsNotOverriddenArrayNested2={new YourClass(), new YourClass()};

the following assertions are true

 assertFalse(Arrays.equals(equalsNotOverriddenArrayNested1, equalsNotOverriddenArrayNested2));
  assertFalse(Arrays.deepEquals(equalsNotOverriddenArrayNested1, equalsNotOverriddenArrayNested2));
  

Equals overridden

Non-Nested

While doing an Arrays.equals for non-nested ‘overridden equals’ objects, it can be said that if Arrays.equals is true, then Arrays.deepEquals also return true.

Given two String arrays

assertTrue(Arrays.equals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));
assertTrue(Arrays.deepEquals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));

the following assertions are true

assertTrue(Arrays.equals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));
assertTrue(Arrays.deepEquals(equalsOverriddenArrayNonNested1, equalsOverriddenArrayNonNested2));

since they are just one-to-one equals call on each pair.

Nested

Interesting scenario : While doing an Arrays.equals for nested ‘overridden equals’ objects, if Arrays.equals is false, then Arrays.deepEquals need not be false.

Consider two Object arrays which has two values - a String and a String array

assertFalse(Arrays.equals(equalsOverriddenArrayNested1, equalsOverriddenArrayNested2));
assertTrue(Arrays.deepEquals(equalsOverriddenArrayNested1, equalsOverriddenArrayNested2));

The result for Arrays.deepEquals is logical since each (from the source), the method loops through each pair of elements, checks whether it is an array type and calls deepEquals on each of the pair. If it is an non-array type, then it just calls the equals on the object.

However, the result of Arrays.equals is tricky but at the same time obvious. The Arrays.equals method blindly calls equals on each pair and since the second arguments String[] are of Object type (whose equals is not overridden), it checks for reference equality and fails !!

The entire testcase can be found below :

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









Object (computer science)

Published at DZone with permission of Arun Manivannan, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How to Create a Real-Time Scalable Streaming App Using Apache NiFi, Apache Pulsar, and Apache Flink SQL
  • Beginners’ Guide to Run a Linux Server Securely
  • Continuous Development: Building the Thing Right, to Build the Right Thing
  • 7 Awesome Libraries for Java Unit and Integration Testing

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: