38

How can I check if two ArrayLists differ from one another? I don't care what's the difference, I just want to know if they're not the same.

I'm fetching scores list from a database every minute, and only if the scores list that I fetched is different from the one I fetched a minute ago I want to send it to the client.

Now the value of the ArrayList is actually a class that I created (that contains name, lvl, rank, score).

Do I need to implement equals() on it?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
ufk
  • 30,912
  • 70
  • 235
  • 386
  • 2
    What is "same"? Is a list `[1, 2, 3]` the same as `[1.0, 2.0, 3.0]`? What about `[3, 2, 1]`? Or `["one", "two", "three"]`? If you don't care to define what counts as same/difference, we can just say use `==` and `!=`. At least that's _one_ definition of whether or not two objects are the same. – polygenelubricants Jun 07 '10 at 13:47
  • @polygenelubricants: Unless specified otherwise "are those the same" means `equals()` in Java. In don't see why this should be put in question here. – Joachim Sauer Jun 07 '10 at 13:48
  • 2
    @Joachim: if OP means to define equality using `equals`, then OP would already know the answer and wouldn't ask the question in the first place, right? (i.e. Q: How can I check if `list1` is `equals` to `list2`? A: Use `list1.equals(list2)`) – polygenelubricants Jun 07 '10 at 13:50
  • i require exact same values with same types and same order – ufk Jun 07 '10 at 13:56
  • 1
    equals() it is, then. Just make sure that your values are of a class which defines an equals-function and you should be fine. – Lizzan Jun 07 '10 at 14:03
  • @Lizzan - there are classes that don't define equals? How can I achieve this? – kgrad Jun 07 '10 at 14:07
  • @kgrad: that's of course not possible, but there are many classes that define `equals()` based on object identity alone (such as `java.lang.Object`). A colloquial shortcut for that behaviour is "classes that don't implement `equals()`". – Joachim Sauer Jun 07 '10 at 14:11
  • @ufk: I'm still curious what your use case is, because if you need a way to make sure that a list has not been modified at all for e.g. security purposes, then `equals` is not a foolproof way of doing it. The "I don't care what's changed" part is what needs to be clarified, because there can be a lot of changes to a list that would not be detectable by `equals`. – polygenelubricants Jun 07 '10 at 14:23
  • it's a scores array that i fetch from database. i want to send it to the client only if it was changed from the previous time i fetched the scores from db. The Values are from a class that I created, do i need to implement equals on it ? – ufk Jun 07 '10 at 14:33

6 Answers6

81

On the definition of "sameness"

As Joachim noted, for most application, List.equals(Object o) definition works:

Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order. This definition ensures that the equals method works properly across different implementations of the List interface.

Depending on how you're using it, though, this may not work as expected. If you have a List<int[]>, for example, it doesn't quite work because arrays inherit equals from Object which defines equality as reference identity.

    List<int[]> list1 = Arrays.asList(new int[] { 1, 2, 3 });
    List<int[]> list2 = Arrays.asList(new int[] { 1, 2, 3 });
    System.out.println(list1.equals(list2)); // prints "false"

Also, two lists with different type parameter can be equals:

    List<Number> list1 = new ArrayList<Number>();
    List<String> list2 = new ArrayList<String>();
    System.out.println(list1.equals(list2)); // prints "true"

You also mentioned that the list must contain elements with the same type. Here's yet another example where the elements don't have the same type, and yet they're equals:

    List<Object> list1 = new ArrayList<Object>();
    List<Object> list2 = new ArrayList<Object>();
    list1.add(new ArrayList<Integer>());
    list2.add(new LinkedList<String>());
    System.out.println(list1.equals(list2)); // prints "true"

So unless you clearly define what equality means to you, the question can have very different answers. For most practical purposes, though, List.equals should suffice.


On implementing equals

Information after update suggests that List.equals will do the job just fine, provided that the elements implement equals properly (because List<E>.equals invokes E.equals on the non-null-elements, per the API documentation above).

So in this case, if we have, say, a List<Player>, then Player must @Override equals(Object o) to return true if o instanceof Player and on the relevant fields, they're all equals (for reference types) or == (for primitives).

Of course, when you @Override equals, you should also @Override int hashCode(). The barely acceptable minimum is to return 42;; slightly better is to return name.hashCode();; best is to use a formula that involves all the fields on which you define equals. A good IDE can automatically generate equals/hashCode methods for you.

See also

  • Effective Java 2nd Edition
    • Item 8: Obey the general contract when overriding equals
    • Item 9: Always override hashcode when you override equals

API links

Related questions

On equals/hashCode combo:

On equals vs ==:

Community
  • 1
  • 1
polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
13

Use equals(). As long as the elements inside the lists implement equals() correctly it will return the correct values.

Unless you want to ignore the order of the values, then you should dump the values in two Set objects and compare those using equals().

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • 2
    If you put the values in a set, you remove duplicates... You will not be checking if they are the same, only if they contain the same elements. This may or may not be what OP wants. But in my opinion, if one list contains 3 x's and one contains only 2, they are not the same. – kgrad Jun 07 '10 at 13:45
  • 1
    Well, check the length of the two lists first, then dump them into sets if that's what you want. – Lizzan Jun 07 '10 at 13:47
  • 2
    @Lizzan: That won't work either. The lists ["a", "a", "b"] and ["a", "b", "b"] have the same length, would be equal if converted to sets, but are still not equal for any reasonable definition of equality of lists. – jarnbjo Jun 07 '10 at 13:54
  • Right, but if the OP wants to check for that, equals() will work just fine, as Joachim said first. On the other hand, that does make my previous comment rather unuseful as well. – Lizzan Jun 07 '10 at 14:02
10

Here's a simple method that checks if 2 Array Lists contain the same values regardless their order.

 //the name of the method explains it well...
    public boolean isTwoArrayListsWithSameValues(ArrayList<Object> list1, ArrayList<Object> list2)
    {
        //null checking
        if(list1==null && list2==null)
            return true;
        if((list1 == null && list2 != null) || (list1 != null && list2 == null))
            return false;

        if(list1.size()!=list2.size())
            return false;
        for(Object itemList1: list1)
        {
            if(!list2.contains(itemList1))
                return false;
        }

        return true;
    }
Traveling Salesman
  • 2,209
  • 11
  • 46
  • 83
2

As @Joachim Sauer mentioned in his answer, equals should work if the lists are equal and their contents implement equals correctly. But, it shouldn't work if the items are not in the same "order" since it doesn't use contains for the check. In this sense, it checks for "strict" equality as mentioned by @jarnbjo

        //From android's Arraylist implementation
        Iterator<?> it = that.iterator();
        for (int i = 0; i < s; i++) {
            Object eThis = a[i];
            Object eThat = it.next();
            if (eThis == null ? eThat != null : !eThis.equals(eThat)) {
                return false;
            }
        }

However, I wanted somewhat different behaviour, I didn't care about order or anything like that. All I wanted was to be sure the two didn't contain the same items. My solution,

    //first check that both are not null and are of same length. (not shown here)
    //if both match, pull out the big guns as below
    ...
    List<Object> comparedList = new ArrayList<>(listOne);
    comparedList.removeAll(listTwo);
    if(comparedList.size() != 0) //there are differences between the two

This is less performant since it loops twice, first in removeAll and then in contains which is called by removeAll.

My list was guaranteed to be short so I didn't mind the hit.

frostymarvelous
  • 2,786
  • 32
  • 43
2

You can convert them to string and then compare like

list1.toString().equals(list2.toString())
Robert Londo
  • 21
  • 1
  • 2
-1

You can also check the Arraylist as shown below:

public  boolean equalLists(List<String> one, List<String> two){     
if (one == null && two == null){
    return true;
}

if((one == null && two != null) 
  || one != null && two == null
  || one.size() != two.size()){
    return false;
}

//to avoid messing the order of the lists we will use a copy
//as noted in comments by A. R. S.
one = new ArrayList<String>(one); 
two = new ArrayList<String>(two);   

Collections.sort(one);
Collections.sort(two);      
return one.equals(two);
}

Thanks to @Jacob

Community
  • 1
  • 1
Aristo Michael
  • 2,166
  • 3
  • 35
  • 43