Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we're going to have a look at different ways to compare arrays in Java. We'll cover conventional methods, and we'll also see some examples using lambda expressions.

2. Comparing Arrays

We're going to compare arrays in Java, and as we know, these are objects. Therefore, let's refresh some basic concepts:

  • Objects have references and values
  • Two equal references should point to the same value
  • Two different values should have different references
  • Two equal values don't necessarily have the same references
  • Primitive values only get compared per value
  • String literals only get compared per value

2.1. Comparing Object References

If we have two references pointing to the same array, we should always get a result true in an equals comparison with the == operator.

Let's look at an example:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = planes1;

First, we created an array of plane models referenced by planes1. We then create planes2, which references planes1. By doing this, we're creating two references to the same array in memory. Therefore, the “planes1 == planes2” expression will return true.

For arrays, the equals() method is the same as the == operator. So, planes1.equals(planes2) returns true because both references are referring to the same object. Generally speaking, array1.eqauls(array2) will return true if and only if the expression array1 == array2″ returns true.

Let's assert if the two references are the same:

assertThat(planes1).isSameAs(planes2);

Let's now be sure that the values referenced by planes1 are actually the same as those referenced by planes2. Therefore, we can change the array referenced by planes2, and check if the changes have any impact on the array referenced by planes1:

planes2[0] = "747";

To finally see this working, let's make our assertions:

assertThat(planes1).isSameAs(planes2);
assertThat(planes2[0]).isEqualTo("747");
assertThat(planes1[0]).isEqualTo("747");

With this unit test, we were able to compare two arrays by reference.

However, we've only proven that one reference, once assigned to the value of another, will reference the same value.

We'll now create two different arrays with the same values:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };

Since they are different objects, we know for sure that they are not the same. We can, therefore, compare them:

assertThat(planes1).isNotSameAs(planes2);

To sum up, in this case, we have two arrays in memory that contain the same String values in exactly the same order. However, not only are the referenced arrays different in content, but the references themselves are also different.

2.2. Comparing Array Lengths

The length of the arrays can be compared regardless of their element types, or whether or not their values are filled in.

Let's create two arrays:

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

These are two different arrays with different element types. In this data set, we're registering, as an example, how many airplanes of each model are stored in the warehouse. Let's now run unit tests on them:

assertThat(planes1).hasSize(8);
assertThat(quantities).hasSize(8);

With this, we've proven that both arrays have eight elements and that the length property returns the correct number of elements for each array.

2.3. Comparing Arrays with Arrays.equals

So far we only compared arrays based on their object identities. On the other hand, to check if two arrays are equal in terms of their contents, Java provides the Arrays.equals static method. This method will iterate through the arrays, per position in parallel, and apply the == operator, for every pair of elements.

Let's create two different arrays with the same String literals in exactly the same order:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };

And now, let's assert that they're equal:

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

If we change the order of the values of the second array:

String[] planes1 = new String[] { "A320", "B738", "A321", "A319", "B77W", "B737", "A333", "A332" };
String[] planes2 = new String[] { "B738", "A320", "A321", "A319", "B77W", "B737", "A333", "A332" };

We'll get a different result:

assertThat(Arrays.equals(planes1, planes2)).isFalse();

2.4. Comparing Arrays with Arrays.deepEquals

Using the == operator is easy if we're using simple types in Java. These could be primitive types or String literals. A comparison between arrays of Objects can be more complicated. The reason behind this is fully explained in our Arrays.deepEquals article. Let's see an example.

First, let's start with a Plane class:

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

    // getters and setters
}

And, let's implement the hashCode and equals methods:

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

Secondly, let's create the following two-element arrays:

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

Let's now see if they are true, deeply equal arrays:

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

To make sure that our comparison works as  expected, let's now change the order of our last array:

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

Finally, let's test if they are indeed not equal anymore:

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

2.5. Comparing Arrays with Different Orders of Elements

To check if arrays are equal, regardless of the order of elements, we need to define what makes one instance of our Plane unique. For our case, a different name or model is enough to determine that one plane is different from another. We've established this by having already implemented both hashCode and equals methods. This implies that before we can compare our arrays, we should sort them. For that, we need a Comparator:

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

In this Comparator, we're giving priority to the name. If the names are equal, we solve the ambiguity by looking at the model. We compare strings by using the compareTo method of type String.

We want to be able to find if arrays are equal regardless of the sorting order. To do that, let's now sort our arrays:

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

And finally, let's test them:

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

Having sorted the arrays in the same order first, we allow the deepEquals method to find if these two arrays are equal.

3. Conclusion

In this tutorial, we've seen different ways of comparing arrays. Secondly, we saw the difference between comparing references and values. In addition, we've taken a look into how we can compare arrays deeply. Finally, we saw the difference between a normal compare and a deep compare using equals and deepEquals, respectively.

As always, the full source code for the examples is available over on GitHub.

Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Fantaman
Fantaman
2 months ago

The article seems to miss the main point: that you should not use array1.equals(array2). It just mentions that there is Arrays.equals(), but it’s not motivated. Also the example for deepEquals() is unnecessarily “complicated”: why not use a 2d array of integers or Strings? It would illustrate the problem in the same way while being easier/faster to understand. And it would not convey the notion that you need deepEquals() only if you deal with complex objects (instead of e.g. primitive types).

Loredana Crusoveanu
2 months ago
Reply to  Fantaman

Hi Fantaman,
The main point of the article is to enumerate different ways of comparing arrays. However, there should be a mention of why array1.equals(array2) shouldn’t be used. We’ll update the article.

About your point with deepEquals(), the example is indeed more complicated, as we wanted to highlight the equals and hashCode methods. Although, our main article on the subject, which is also referenced in the article, explains it with a String-based example as well.

Comments are closed on this article!