Comparing two lists in kotlin

65,509

Solution 1

Just fyi you can call list1 == list2 without any extra work, if your custom object is based off of a data class (which automatically overrides equals for you).

Solution 2

If you don't bother about order of elements in both lists, and your goal is to just check that two lists are of exactly same elements, without any others, you can consider two mutual containsAll calls like:

var list1 = mutableListOf<String>()
var list2 = mutableListOf<String>()

if(list1.containsAll(list2) && list2.containsAll(list1)) {
    //both lists are of the same elements
}

Solution 3

Java lists implement equals method and two lists are defined to be equal if they contain the same elements in the same order. I guess, you are missing equals method in your MyObject class.

Solution 4

Using zip

zip returns a list of pairs built from the elements of this array and the other array with the same index. The returned list has length of the shortest collection.

fun listsEqual(list1: List<Any>, list2: List<Any>): Boolean {

    if (list1.size != list2.size)
        return false

    val pairList = list1.zip(list2)

    return pairList.all { (elt1, elt2) ->
        elt1 == elt2       
    }
}

Solution 5

You can use implementations below for comparing of two Collection:

infix fun <T> Collection<T>.deepEqualTo(other: Collection<T>): Boolean {
    // check collections aren't same
    if (this !== other) {
        // fast check of sizes
        if (this.size != other.size) return false
        val areNotEqual = this.asSequence()
            .zip(other.asSequence())
            // check this and other contains same elements at position
            .map { (fromThis, fromOther) -> fromThis == fromOther }
            // searching for first negative answer
            .contains(false)
        if (areNotEqual) return false
    }
    // collections are same or they are contains same elements with same order
    return true
}

Or order ignore variant:

infix fun <T> Collection<T>.deepEqualToIgnoreOrder(other: Collection<T>): Boolean {
    // check collections aren't same
    if (this !== other) {
        // fast check of sizes
        if (this.size != other.size) return false
        val areNotEqual = this.asSequence()
            // check other contains next element from this
            .map { it in other }
            // searching for first negative answer
            .contains(false)
        if (areNotEqual) return false
    }
    // collections are same or they are contains same elements
    return true
}

Note: both function compare only first level of deep

Share:
65,509
Aswin
Author by

Aswin

Tech-knowledge-y freak.

Updated on July 09, 2022

Comments

  • Aswin
    Aswin almost 2 years

    I came across with kotlin equals function to compare two list of same type. It works fine for pure Kotlin with data classes.

    I'am using a Java library in Kotlin project in which a callback method returns a list of objects for a time interval of X seconds. Trying to compare the old list with new list for every call, but equals returns false even the items are same and equal.

    val mOldList: MutableList<MyObject>()? = null
    
    override fun updatedList(list: MutableList<MyObject>){
        // other code
        if (mOldList.equals(list)) // false everytime
    }
    

    Is this because of Java's equals method from library?

    Alternative suggestions for list compare would be appreciative.

  • Aswin
    Aswin over 5 years
    MyObject is a class from library, so I cannot override equals() method.
  • Henry
    Henry about 5 years
    You could always extend MyObject and then override equals(), if possible.
  • Hola Soy Edu Feliz Navidad
    Hola Soy Edu Feliz Navidad over 4 years
    That's the most logical answer.
  • shabunc
    shabunc over 4 years
    I'm downvoting this - this is definitely not the best way to do it.
  • DruidKuma
    DruidKuma over 4 years
    @shabunc Could you please state what is wrong about this way?
  • shabunc
    shabunc over 4 years
    @DruidKuma because instead of O(n) comparisons you basically calling containsAll twice and this just can not have better performance than linear comparison per index. - check this for instance - stackoverflow.com/questions/10199772/…
  • Dilroop Singh
    Dilroop Singh about 4 years
    answer mentions if you dont bother the order of elements
  • Jakob Ulbrich
    Jakob Ulbrich almost 4 years
    To break early and avoid iterating over the whole list, you should use this.asSequence().mapIndexed {...}
  • Manushin Igor
    Manushin Igor almost 4 years
    This method allocates several unnesesary objects: at least one new list and new Pair per each element from the longest list. In addition several enumerators are allocated, however JIT can put them on stack. So this method requires O(N) additional memory in heap.
  • Manushin Igor
    Manushin Igor almost 4 years
    This method can fail if lists have different sizes. Also this method allocates new list in memory.
  • amynbe
    amynbe almost 4 years
    Good point. In that regard, @XIII-th 's answer below is better stackoverflow.com/a/58310635/326162
  • Manushin Igor
    Manushin Igor almost 4 years
    Second answer has O(N^2) complexity. Statement it in other has O(N) complexity for list and it is called N times. Right solution for second case is something like return this.toSet() == other.toSet()
  • Sergei Bubenshchikov
    Sergei Bubenshchikov almost 4 years
    @ManushinIgor yes, your solution is better the thаn my. Thanks
  • Manushin Igor
    Manushin Igor almost 4 years
    Instead of List, Set should be used. HashSet (not TreeSet - it is important) collections have O(1) lookup complexity (list have O(N)). So just replace both mutableListOf to mutableSetOf.
  • Manushin Igor
    Manushin Igor almost 4 years
    Please note, that this function requires the same elements order for true answer.
  • handhand
    handhand over 3 years
    If there are no duplicates in your list, you can compare the sizes first, then one containsAll() will be enough.
  • Sai
    Sai over 3 years
    Thanks. And for the people who wonder about this listOfdataClass1.map { it.string } == listOfdataClass2.map { it.string } also works as expected.
  • Sira Lam
    Sira Lam about 3 years
    Performance wise, containsAll() is very slow. Sort and check equals is way faster.
  • Salim Mazari Boufares
    Salim Mazari Boufares over 2 years
    This won't work if the elements don't have the same order, an option is to sort both lists before to compare them.
  • Farid
    Farid over 2 years
    Yeah, easy-peasy. What happens if MyObject is final?
  • Dakshinamurthy Karra
    Dakshinamurthy Karra over 2 years
    Use a decorator?