I recently published Difference, a Kotlin library that lets you compare two lists to determine a sequence of change operations between the two. Many Android developers have already been generating diffs to animate changes in their RecyclerViews, but there are many other reasons you might want to compare two lists. Maybe you’re working on an app that needs to detect differences between two files like git does. Or maybe a backend you’re integrating with sends you a before and after state, but your app needs to show only what changed.

Regardless of your use-case, Difference is here to help.

Why not use DiffUtil?

The first question you’re probably asking is, “Why on earth would you make a list diffing library? You’re an Android developer and Google already made DiffUtil.” I could answer this question by saying “Because I am insane and like making things more complicated than they need to be,” and while that accounts for most of my decisions in general, it avoids the bigger picture here.

If you’re using DiffUtil with a RecyclerView and you don’t have a need for diffs in any other context, then everything is great and you should keep using DiffUtil. But the big problem with DiffUtil is that it’s coupled with RecyclerView, and that has some serious drawbacks for other use cases.

DiffUtil is not cross-platform

DiffUtil is a Java library that has a dependency on RecyclerView. Those two facts already limit its usage to the universe of Android app projects. Even if you coerce your compiler and coworkers to let you use DiffUtil with a vanilla Java project, that’s as much cross-compatibility you’ll get from it. This is especially unfortunate because the diffing algorithm at its core has no Android or even Java dependencies to function properly.

This is where Difference comes in. Difference is a Kotlin multiplatform library that’s platform independent. Regardless of if you’re developing for web, native, Android or iOS, you can use Difference in your project — which you can’t say about DiffUtil.

DiffUtil depends on RecyclerView

DiffUtil is included as part of the RecyclerView library, meaning that if you want DiffUtil you also need to pull in RecyclerView. The real issue is that if you’re bringing in RecyclerView, you also end up pulling in the entire universe that is the appcompat library.

For many apps this isn’t a big deal whatsoever. After all, you’re likely already using RecyclerView, so what’s the big deal with some clever reuse of DiffUtil since we already know how to use it?

But if you step into your DeLorean with me for a moment and visit the year 2021 or 2022, you’ll be seeing Jetpack Compose in a lot of places. (Or so we can hope. My crystal ball doesn’t keep me in the loop about software release dates and adoption rates.)

If you’re building an app that uses Jetpack Compose, you probably don’t want to be bringing in RecyclerView for the sake of getting DiffUtil. In this utopian future, adding RecyclerView to your project means pulling in a lot of code centered around a UI framework that you have no intention of using. R8 can minify these classes away for you, but that means adding a lot of unnecessary complexity to your build pipeline to make sure you’re not bloating your APK size with code you’ll never use. It’s much easier if you never add RecyclerView to your project to begin with if your app exclusively uses Compose. If you’re one of these lucky full-time composers, then Difference is for you.

How does Difference compare to DiffUtil?

Difference is a Kotlin multiplatform library that implements Eugene Myers’ differencing algorithm — the same algorithm that DiffUtil implements. Difference will detect insertions, deletions, and (optionally) moves. It also uses similar optimizations as DiffUtil to avoid recursion and to reduce the amount of memory allocated by the algorithm.

If you like big-O notation, the worst case runtime of the algorithm takes on the order of O((M+N)×D + D log D) operations. M and N are the lengths of the two input lists, and D is the smallest number of operations that it takes to modify the original list into the updated one. If move detection is enabled, add another O(D²) to that runtime.

Difference is written from the ground-up for Kotlin — which is to say that I did not open DiffUtil, press ⌘+⇧+K, and call it a day. It takes advantage of Kotlin language features like lambda expressions and default parameters to allow for concise code. It does not use any external dependencies, and can be compiled to run on any platform that can run Kotlin code.

Start using Difference

If I’ve kept your attention thus far, then you’re likely one of the lucky individuals with a use case for Difference. If you’re using Gradle, you can use this line to add Difference to your project:

implementation "dev.andrewbailey.difference:difference:1.0.0"

When you need to compute a diff for two sets of data, you can call Difference like this:

val listOfBffs = listOf("Merengue", "Sherb", "Tammy")
val revisedListOfBffs = listOf("Merengue", "Sprinkle", "Sherb")

val diff = differenceOf(
    original = listOfBffs,
    updated = revisedListOfBffs,
    detectMoves = false
)

The returned diff object will contain a list of operations that, when applied in order on the original list, will yield the updated list. In the example above, the diff would look like this:

Insert(index = 1, item = "Sprinkle")
Remove(index = 3)

If you’re building a UI-based application, be careful when using Difference since diff generation can easily exceed 16ms for arbitrary lists which can lead to dropped frames. Because of the runtime complexity of diff generation, I highly recommend using Difference on a background thread to keep your UI smooth. If you’re using Coroutines, then you can easily offload diff generation to a background thread like this:

scope.launch {
    val listOfBffs = ...
    val revisedListOfBffs = ...

    val diff = withContext(Dispatchers.Default) {
        differenceOf(...)
    }

    // Do whatever you need with the diff
    diff.applyDiff(
        remove = { index -> ... },
        insert = { item, index -> },
        move = { oldIndex, newIndex -> ... }
    )
}

You can find the source code and documentation for Difference on GitHub. Give it a shot the next time you need to compare two lists and let me know what you think!