239

When comparing arrays in Java, are there any differences between the following 2 statements?

Object[] array1, array2;
array1.equals(array2);
Arrays.equals(array1, array2);

And if so, what are they?

Laser Infinite
  • 253
  • 1
  • 15
PandaConda
  • 3,396
  • 2
  • 20
  • 22

9 Answers9

340

array1.equals(array2) is the same as array1 == array2, i.e. is it the same array. As @alf points out it's not what most people expect.

Arrays.equals(array1, array2) compares the contents of the arrays.


Similarly array.toString() may not be very useful and you need to use Arrays.toString(array).

das-g
  • 9,718
  • 4
  • 38
  • 80
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 66
    Note that `Arrays.equals()` does not work as expected for multidimensional arrays, it only compares items of the 1st dimension for reference equality. Apache commons `ArrayUtils.isEquals` works with multidimensional arrays. – Adam Parkin Mar 18 '13 at 17:43
  • 7
    I'm stunned. Is there a reason for array.equals to be implemented to pointer comparison rather than comparing length and every object? – Lake Oct 29 '13 at 09:34
  • 2
    @Lake is does compare the array length and the objects contained, but what it doesn't do is a deep comparison. The fact equals does work as expected for arrays is broken, this shouldn't be an issue in the first place. – Peter Lawrey Oct 29 '13 at 18:01
  • 52
    @AdamParkin That why we have [`Arrays.deepEquals(Object[], Object[])`](http://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#deepEquals%28java.lang.Object[],%20java.lang.Object[]%29). – Elliott Frisch Oct 18 '14 at 20:04
  • 1
    According to the JAVA API, [https://docs.oracle.com/javase/7/docs/api/java/util/Arrays.html#equals(double[],%20double[])] Two arrays are considered equal if both arrays contain the same number of elements, and all corresponding pairs of elements in the two arrays are equal. In other words, two arrays are equal if they contain the same elements in the **same order**. Also, two array references are considered equal if both are null. Order of elements matters even if both arrays holds the same objects or primitive data. – G 1 Aug 01 '16 at 05:47
  • 3
    @JeewanthaSamaraweera that is the definition for that method, however for `.equals` it doesn't compare contents which is why you need that method. – Peter Lawrey Aug 01 '16 at 07:31
  • 1
    Sadly, `HashSet` is useless. – Dávid Horváth Aug 03 '17 at 23:39
  • @DávidHorváth you need to create a wrapper class for `int[]` – Peter Lawrey Aug 04 '17 at 09:39
  • My question is Why is that array1.equals(array2) doesn't work? Is it a feature and by design , Oracle/Sun Microsystems made it that way? – sofs1 Aug 02 '18 at 04:29
  • @user3705478 it works as they expected which is to only compare the references. Sun favoured doing this for all mutable data types and only compared contents for immutable types eg String vs StringBuilder. IMHO a more pragmatic approach would have been better. – Peter Lawrey Aug 02 '18 at 05:54
  • Note that the problem also exists for Objects having a field of any array type. They should override the equals method to compare the array field with Arrays.equals(). – very Dec 20 '18 at 10:57
92

It's an infamous problem: .equals() for arrays is badly broken, just don't use it, ever.

That said, it's not "broken" as in "someone has done it in a really wrong way" — it's just doing what's defined and not what's usually expected. So for purists: it's perfectly fine, and that also means, don't use it, ever.

Now the expected behaviour for equals is to compare data. The default behaviour is to compare the identity, as Object does not have any data (for purists: yes it has, but it's not the point); assumption is, if you need equals in subclasses, you'll implement it. In arrays, there's no implementation for you, so you're not supposed to use it.

So the difference is, Arrays.equals(array1, array2) works as you would expect (i.e. compares content), array1.equals(array2) falls back to Object.equals implementation, which in turn compares identity, and thus better replaced by == (for purists: yes I know about null).

Problem is, even Arrays.equals(array1, array2) will bite you hard if elements of array do not implement equals properly. It's a very naive statement, I know, but there's a very important less-than-obvious case: consider a 2D array.

2D array in Java is an array of arrays, and arrays' equals is broken (or useless if you prefer), so Arrays.equals(array1, array2) will not work as you expect on 2D arrays.

starball
  • 20,030
  • 7
  • 43
  • 238
alf
  • 8,377
  • 24
  • 45
  • 16
    It's not broken, it's just inherited from Object. – Michael Borgwardt Jan 08 '12 at 11:56
  • Does an array have a custom implementation for `equals()`? I thought is wasn't overridden from Object. – Martijn Courteaux Jan 08 '12 at 11:57
  • @MichaelBorgwardt it's a system library, with a method which does not do what's said in the javadoc. Sounds broken enough to me. That said, I do admit it's a very arguable statement, but I believe that "it's broken" is remembered better, and thus it's much more convenient to think of it this way. – alf Jan 08 '12 at 12:01
  • @MartijnCourteaux that's exactly the problem :) – alf Jan 08 '12 at 12:02
  • @alf: There's no Javadoc for arrays, the language specification says it inherits the methods from Object, and the equals() inherited from Object does exactly what it says in its Javadoc. The statement "it's broken" is simply uninformative bordering on ignorant. – Michael Borgwardt Jan 08 '12 at 12:06
  • 3
    For arrays of arrays, you need [`Arrays.deepEquals`](https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#deepEquals-java.lang.Object:A-java.lang.Object:A-) --- it's what `someArray.equals` should have done all along. (Related: [`Objects.deepEquals`](https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#deepEquals-java.lang.Object-java.lang.Object-).) – Kevin J. Chase Oct 24 '16 at 05:29
  • Even Arrays.equals and Arrays.deepEquals are pretty broken, for my money. Arrays.equals is overloaded for all primitives and for T, but you can't just pass two Objects in, so it's no use for any form of reflection (unlike the methods of Array, such Array.getLength(Object) etc.). And strangely, it's missing from Array. Arrays.deepEquals() takes Object[]s, but you can't just cast your array objects to Object[] as this fails where the components are primitives. – barneypitt Oct 22 '22 at 17:39
20

Look inside the implementation of the two methods to understand them deeply:

array1.equals(array2);
/**
 * Indicates whether some other object is "equal to" this one.
 * <p>
 * The {@code equals} method implements an equivalence relation
 * on non-null object references:
 * <ul>
 * <li>It is <i>reflexive</i>: for any non-null reference value
 *     {@code x}, {@code x.equals(x)} should return
 *     {@code true}.
 * <li>It is <i>symmetric</i>: for any non-null reference values
 *     {@code x} and {@code y}, {@code x.equals(y)}
 *     should return {@code true} if and only if
 *     {@code y.equals(x)} returns {@code true}.
 * <li>It is <i>transitive</i>: for any non-null reference values
 *     {@code x}, {@code y}, and {@code z}, if
 *     {@code x.equals(y)} returns {@code true} and
 *     {@code y.equals(z)} returns {@code true}, then
 *     {@code x.equals(z)} should return {@code true}.
 * <li>It is <i>consistent</i>: for any non-null reference values
 *     {@code x} and {@code y}, multiple invocations of
 *     {@code x.equals(y)} consistently return {@code true}
 *     or consistently return {@code false}, provided no
 *     information used in {@code equals} comparisons on the
 *     objects is modified.
 * <li>For any non-null reference value {@code x},
 *     {@code x.equals(null)} should return {@code false}.
 * </ul>
 * <p>
 * The {@code equals} method for class {@code Object} implements
 * the most discriminating possible equivalence relation on objects;
 * that is, for any non-null reference values {@code x} and
 * {@code y}, this method returns {@code true} if and only
 * if {@code x} and {@code y} refer to the same object
 * ({@code x == y} has the value {@code true}).
 * <p>
 * Note that it is generally necessary to override the {@code hashCode}
 * method whenever this method is overridden, so as to maintain the
 * general contract for the {@code hashCode} method, which states
 * that equal objects must have equal hash codes.
 *
 * @param   obj   the reference object with which to compare.
 * @return  {@code true} if this object is the same as the obj
 *          argument; {@code false} otherwise.
 * @see     #hashCode()
 * @see     java.util.HashMap
 */
public boolean equals(Object obj) {
    return (this == obj);
}

while:

Arrays.equals(array1, array2);
/**
 * Returns <tt>true</tt> if the two specified arrays of Objects are
 * <i>equal</i> to one another.  The two arrays are considered equal if
 * both arrays contain the same number of elements, and all corresponding
 * pairs of elements in the two arrays are equal.  Two objects <tt>e1</tt>
 * and <tt>e2</tt> are considered <i>equal</i> if <tt>(e1==null ? e2==null
 * : e1.equals(e2))</tt>.  In other words, the two arrays are equal if
 * they contain the same elements in the same order.  Also, two array
 * references are considered equal if both are <tt>null</tt>.<p>
 *
 * @param a one array to be tested for equality
 * @param a2 the other array to be tested for equality
 * @return <tt>true</tt> if the two arrays are equal
 */
public static boolean equals(Object[] a, Object[] a2) {
    if (a==a2)
        return true;
    if (a==null || a2==null)
        return false;

    int length = a.length;
    if (a2.length != length)
        return false;

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

    return true;
}
Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417
14

Sigh. Back in the 70s I was the "system programmer" (sysadmin) for an IBM 370 system, and my employer was a member of the IBM users group SHARE. It would sometimes happen thatsomebody submitted an APAR (bug report) on some unexpected behavior of some CMS command, and IBM would respond NOTABUG: the command does what it was designed to do (and what the documentation says).

SHARE came up with a counter to this: BAD -- Broken As Designed. I think this might apply to this implementation of equals for arrays.

There's nothing wrong with the implementation of Object.equals. Object has no data members, so there is nothing to compare. Two "Object"s are equal if and only if they are, in fact, the same Object (internally, the same address and length).

But that logic doesn't apply to arrays. Arrays have data, and you expect comparison (via equals) to compare the data. Ideally, the way Arrays.deepEquals does, but at least the way Arrays.equals does (shallow comparison of the elements).

So the problem is that array (as a built-in object) does not override Object.equals. String (as a named class) does override Object.equals and give the result you expect.

Other answers given are correct: [...].equals([....]) simply compares the pointers and not the contents. Maybe someday somebody will correct this. Or maybe not: how many existing programs would break if [...].equals actually compared the elements? Not many, I suspect, but more than zero.

A. P. Damien
  • 376
  • 2
  • 10
7

Arrays inherit equals() from Object and hence compare only returns true if comparing an array against itself.

On the other hand, Arrays.equals compares the elements of the arrays.

This snippet elucidates the difference:

Object o1 = new Object();
Object o2 = new Object();
Object[] a1 = { o1, o2 };
Object[] a2 = { o1, o2 };
System.out.println(a1.equals(a2)); // prints false
System.out.println(Arrays.equals(a1, a2)); // prints true

See also Arrays.equals(). Another static method there may also be of interest: Arrays.deepEquals().

Adam Zalcman
  • 26,643
  • 4
  • 71
  • 92
1

The equals() of arrays is inherited from Object, so it does not look at the contents of the arrrays, it only considers each array equal to itself.

The Arrays.equals() methods do compare the arrays' contents. There's overloads for all primitive types, and the one for objects uses the objects' own equals() methods.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • 2
    you say "arrays' contents", does this mean multidimensional arrays too? – AlanFoster Jan 08 '12 at 12:00
  • 1
    @AlanFoster: no. Multidimensional arrays are arrays of arrays, which means they method Arrays.equals(Object[], Object[]) will be invoked, which calls the sub-arrays' equals() methods – Michael Borgwardt Jan 08 '12 at 12:02
1

The Arrays.equals(array1, array2) :

check if both arrays contain the same number of elements, and all corresponding pairs of elements in the two arrays are equal.

The array1.equals(array2) :

compare the object to another object and return true only if the reference of the two object are equal as in the Object.equals()

aleroot
  • 71,077
  • 30
  • 176
  • 213
0
import java.util.Arrays;
public class ArrayDemo {
   public static void main(String[] args) {
   // initializing three object arrays
   Object[] array1 = new Object[] { 1, 123 };
   Object[] array2 = new Object[] { 1, 123, 22, 4 };
   Object[] array3 = new Object[] { 1, 123 };

   // comparing array1 and array2
   boolean retval=Arrays.equals(array1, array2);
   System.out.println("array1 and array2 equal: " + retval);
   System.out.println("array1 and array2 equal: " + array1.equals(array2));

   // comparing array1 and array3
   boolean retval2=Arrays.equals(array1, array3);
   System.out.println("array1 and array3 equal: " + retval2);
   System.out.println("array1 and array3 equal: " + array1.equals(array3));

   }
}

Here is the output:

    array1 and array2 equal: false
    array1 and array2 equal: false

    array1 and array3 equal: true
    array1 and array3 equal: false

Seeing this kind of problem I would personally go for Arrays.equals(array1, array2) as per your question to avoid confusion.

Community
  • 1
  • 1
Tayyab
  • 47
  • 1
  • 2
  • 8
  • It seems to be correct but on arrays, the order of the elements are also important. For instance, if you there is another array Object[] array4 = new Object[] { 123, 1 }; with Arrays.equals(array3, array4), it will return false. – jiahao Dec 06 '18 at 06:57
0

I think the Objects.deepEquals(Obj1,Obj2) is the best uniform solution here, if Obj1 and Obj2 are two int arrays, it will call the Arrays.deepEquals0(a, b) method for you. If you are not comparing strings, it will just use the ".equals"("==") traditional method, so it is also very useful when comparing strings.

It will cover those normal usages like butter, no need to remember when to use ().equals(), Arrays.equals, (String a).equals((String b)) or whatsoever.

The time complexity of this Objects.deepEquals operation will be O(n).

    public static boolean deepEquals(Object a, Object b) {
        if (a == b)
            return true;
        else if (a == null || b == null)
            return false;
        else
            return Arrays.deepEquals0(a, b);
    }

Common usages are like: Compare int array, whooo:

int[] num1 = { 1, 2, 3, 4 };
int[] num2 = { 1, 2, 3, 4 };
System.out.println(Objects.deepEquals(num1, num2));

Can also compare 2D arrays, so gooooood:

int[][] nums1 = { { 1, 2 }, { 2, 3 }, { 3, 4 } };
int[][] nums2 = { { 1, 2 }, { 2, 3 }, { 3, 4 } };
System.out.println(Objects.deepEquals(nums1, nums2));

Compare strings, even greater:

String s1 = "sasfd!";
String s2 = "sasfd" + "!";
System.out.println(Objects.deepEquals(s1, s2));

I LOVE UNIVERSALLY APPLICABLE APPROACHES