DiffUtil Part I. How to improve your List’s Adapter

If you’re using notifyDatasetChanged() for lists, maybe this article will show you how to improve your list’s adapter by using DiffUtil.

At first, when I heard about DiffUtil I felt it was a little overwhelming that I had to override a class and some methods, but it really is easier than you might think, and the result looks much cooler than notifyDatasetChanges().

What is DiffUtil?

As per documentation:

DiffUtil is a utility class that calculates the difference between two lists and outputs a list of update operations that converts the first list into the second one.

DiffUtil uses Eugene W. Myers’s difference algorithm to calculate the minimal number of updates to convert one list into another. Myers’s algorithm does not handle items that are moved so DiffUtil runs a second pass on the result to detect items that were moved.

What that means is that if you use DiffUtil, this class will take care of the updates for the list and will try to find the minimal number of updates for the list. And as a bonus, all the changes will animate nicely and everything will look really cool without needing to worry about calling insert or remove to some elements to get the same effect.

How to get it going?

For this example, we will be using a list of songs that will be updated using DiffUtil.

What you need to do is to define a class that takes as parameters the old list and the new list, and inherit the DiffUtil.Callback() abstract class.

After this you need to override four methods:

  • areItemsTheSame ()- to check if the new item and the old item is the same (usually you can make acheck by id)
  • areContentsTheSame() — to check if the content of the new item is the same as the content of the old item ( ==, in Kotlin, or equals, in Java, should to the job here)
  • getOldListSize() — the size of the old list
  • getNewListSize() — the size of the new list

Show me the code!

In this case the DiffUtil class looks like this:

private class PlaylistDiffUtilCallback(private val oldList: List<Song>, private val newList: List<Song>) : DiffUtil.Callback() { companion object { private const val TAG = "PlaylistDiffUtil" } override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldList[oldItemPosition].id == newList[newItemPosition].id } override fun getOldListSize(): Int = oldList.size override fun getNewListSize(): Int = newList.size override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldList[oldItemPosition].name == newList[newItemPosition].name } }

Where the Song object looks like this:

data class Song(val id: Int, var name: String, val band: String) { fun getFullName(): String { return "$name - $band" } }

Now all you need to do is have a method to update the list

fun updatePlaylist(newSongs: List<Song>) { val diffResult = DiffUtil.calculateDiff(PlaylistDiffUtilCallback(songs, newSongs)) diffResult.dispatchUpdatesTo(this) songs.clear() songs.addAll(newSongs) }

Basically you just pass the current list and the new list to the DiffUtil class and the dispatch the updates and all the magic is done. After that, you should update your current list items to the new list items and that’s it.

For this example, I used a shuffle method in the ViewModel in order to update the list and return a LiveData<List<Song>>.

class PlaylistViewModel : ViewModel() { private val songs: MutableLiveData<List<Song>> = MutableLiveData() init { fillUpSongs() } fun getSongsObservable(): LiveData<List<Song>> = songs private fun fillUpSongs() { val newSongs: MutableList<Song> = mutableListOf( Song(1, "Numb", "Linkin Park"), Song(2, "Savior", "Rise Against"), Song(3, "The Pretender", "Foo Fighters"), Song(4, "Viva la Vida", "Coldplay"), Song(5, "Don't look back in anger", "Oasis") ) songs.value = newSongs } fun shuffle() { val shuffled = songs.value?.shuffled() ?: listOf() songs.value = shuffled } }

The result looks something like this:

Conclusion

This is how you can improve your list’s adapter using DiffUtil, and to me, the result is much spectacular than with notifyDatasetChanges().

If you take a closer look at DiffUtil or at the example you might notice two things. The adapter list gets updated after dispatching DiffUtil update and the DiffUtil.Callback() class has one more method to override. More about this in Part II.

Until then check out other experiments that I did.

We are Android Developers.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store