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.
2.6. Comparing Arrays Lexicographically
Java 9 introduced a brand new method in the Arrays utility class called compare(). As the name implies, this method allows us to compare two arrays lexicographically.
In short, the lexicographic comparison is done element by element and refers to the natural order of the elements, such as numerical order for numbers or alphabetical order for strings.
Typically, if the two arrays have the same length, then the comparison is the result of comparing each pair of elements at the same index within the respective arrays.
So, let’s see it in action:
@Test
void givenSameContents_whenCompare_thenCorrect() {
String[] array1 = new String[] { "A", "B", "C" };
String[] array2 = new String[] { "A", "B", "C" };
assertThat(Arrays.compare(array1, array2)).isEqualTo(0);
}
As shown above, compare() returns zero when the given two arrays are equal lexicographically.
On the other hand, if the two arrays have different lengths, then the method compares the two array lengths and returns the result:
@Test
void givenDifferentContents_whenCompare_thenDifferent() {
String[] array1 = new String[] { "A", "B", "C" };
String[] array2 = new String[] { "A", "C", "B", "D" };
assertThat(Arrays.compare(array1, array2)).isLessThan(0);
assertThat(Arrays.compare(array2, array1)).isGreaterThan(0);
assertThat(Arrays.compare(array1, null)).isGreaterThan(0);
}
As we can see, the method returns a value greater than zero when the first given array is lexicographically greater than the second one. Otherwise, it returns a value less than zero.
Furthermore, a null is always lexicographically less than a non-null array.
Please note that the Arrays class offers several overloaded compare() versions for comparing arrays holding primitive data such as boolean, byte, char, and int.
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.