Sitemap

⚡ “RecyclerView Just Got Smarter: DiffUtil vs ListAdapter Showdown”

3 min readJul 30, 2025

--

Press enter or click to view image in full size

The Struggle: “Why is my RecyclerView acting like Windows 98?”

Picture this:
I was working on a RecyclerView that showed a list of users.
Every time I got fresh data from the server, I did the obvious thing:

users = newList
notifyDataSetChanged()

And it worked… sort of.

Except the entire list blinked like a strobe light at a rave party 🎉.
Smooth animations? Gone. Performance? Down the drain.
Users? Confused. Me? Crying in the corner. 😭

That’s when someone whispered:

“Bro, ever heard of DiffUtil?”

Round 1: The Manual DiffUtil Ceremony

So I dived in. And DiffUtil was… powerful. But also needy.

I had to create a whole callback class just to compare old vs new lists:

class UserDiffCallback(
private val oldList: List<User>,
private val newList: List<User>
) : DiffUtil.Callback() {

override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size

override fun areItemsTheSame(oldPos: Int, newPos: Int) =
oldList[oldPos].id == newList[newPos].id

override fun areContentsTheSame(oldPos: Int, newPos: Int) =
oldList[oldPos] == newList[newPos]
}

And then in my adapter:

fun updateUsers(newUsers: List<User>) {
val diffResult = DiffUtil.calculateDiff(UserDiffCallback(users, newUsers))
users = newUsers
diffResult.dispatchUpdatesTo(this)
}

✅ Smooth animations!
✅ Only updated what changed!
❌ Boilerplate like crazy. Felt like I was doing rituals to please RecyclerView gods 🐐.

Round 2: The ListAdapter Glow-Up ✨

Just when I was about to lose hope, I stumbled across ListAdapter.

Get Vaibhav Shakya | Mr Neo’s stories in your inbox

Join Medium for free to get updates from this writer.

And let me tell you…
It was like upgrading from Windows 98 to macOS overnight.

No more juggling old/new lists. No more manual dispatching.
Just define a comparison strategy once, and call submitList() like a boss.

class UserDiffCallback : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User) =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: User, newItem: User) =
oldItem == newItem
}
class UserAdapter : ListAdapter<User, UserAdapter.UserViewHolder>(UserDiffCallback()) {
class UserViewHolder(val binding: ItemUserBinding) :
RecyclerView.ViewHolder(binding.root)
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val user = getItem(position) // 🚀 No manual list management
holder.binding.userName.text = user.name
}
}

Usage? Easy.

adapter.submitList(newList)

Done. Clean. Elegant. No goat sacrifices required 🐐✨.

Side-by-Side Reality Check 🥊

FeatureManual DiffUtilListAdapterBoilerplateLots of codeMinimalAnimations✅✅List ManagementYou handle itAuto-handledUpdate CallupdateUsers(newList)submitList(newList)Sanity LevelLow 😵High 😌

But Wait 🤔 — What if I Don’t Want Diffing?

If you really don’t care about diffs and just want a brute-force reload…
You can give ListAdapter a dummy callback:

class AlwaysDifferentCallback<T> : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(oldItem: T, newItem: T) = false
override fun areContentsTheSame(oldItem: T, newItem: T) = false
}

But then every update = full refresh = no better than notifyDataSetChanged().
Basically: you’re trolling yourself. 🙃

🎯 Final Verdict

  • If you’re maintaining old code → manual DiffUtil is fine.
  • If you’re starting fresh → go ListAdapter and don’t look back.

For me? I’ve moved on.
RecyclerView deserves love, not abuse ❤️.

👉 What about you?
Are you still stuck in manual DiffUtil land, or have you embraced the ListAdapter life?

⚡ Next time you see someone using notifyDataSetChanged(), send them this post. Save a RecyclerView today. 😅

--

--

Vaibhav Shakya | Mr Neo
Vaibhav Shakya | Mr Neo

👨‍💻 Engineer | 🚀 Entrepreneur | AI & Cybersecurity | 🔐 Building secure, scalable apps | ✍️ Writing dev & product lessons | 🌐 VaibhavShakya.com