Q:

How to update an element in ArrayList without knowing index in Android with Firebase Realtime Database?

I have  a simple RecyclerView with CardView as list items. Each item of the list retrieved from Firebase Realtime databse and stored to noteList. Firebase sdk provides callbacks for onChildAdded, onChildChanged and few others. Now I have designed the Adapter for recycler view to take in an ArrayList. I use the callbacks to add or remove items to the noteList whenever the data in Realtime database changes. The onChildChanged method is called whenever a value of particular item changes. I want to reflect this change in noteList and notify the adapter of the same.
Ofcourse, we can naively search for each element in the List matching the changed element's key and update it. But can this be done in a better/efficient way?
class NoteListAdapter(var data: MutableList<Note>, val onItemClick: (Note) -> Unit) : RecyclerView.Adapter<NoteListAdapter.ViewHolder>() {
    override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
        holder!!.bindNoteItem(data[position])
    }

    override fun getItemCount(): Int = data.size

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
        val v = LayoutInflater.from(parent?.context)
                .inflate(R.layout.card_note, parent, false)
        return ViewHolder(v, onItemClick)
    }

    class ViewHolder(v: View, val onItemClick: (Note) -> Unit): RecyclerView.ViewHolder(v) {
        fun bindNoteItem(note: Note) {
            ...
        }
    }
}

HomeActivity.kt
class HomeActivity : AppCompatActivity() {
    lateinit var mDb: DatabaseReference
    val noteList: MutableList<Note> = ArrayList()
    lateinit var adapter: NoteListAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)

        addNoteFAB.setOnClickListener { addNote() }
        mDb = FirebaseDatabase.getInstance().reference

        adapter = NoteListAdapter(noteList, this::editNote)
        noteListRecycler.layoutManager = LinearLayoutManager(this)
        noteListRecycler.adapter = adapter

        loadNotes()
    }

    fun addNote() {
        val intent = Intent(this, EditNoteActivity::class.java)
        intent.putExtra(EditNoteActivity.EXTRA_NOTE, Note())
        startActivity(intent)
    }

    fun loadNotes() {
        val path = "users/" + FirebaseAuth.getInstance().currentUser?.uid + "/notes"
        mDb.child(path ).addChildListener({
// onChildAdded
            dataSnapshot, prevChildName ->
            val note = dataSnapshot?.getValue(Note::class.java)!!
            note.id = dataSnapshot.key
            noteList += note
            adapter.notifyDataSetChanged()
        }, {
// onChildRemoved
            dataSnapshot ->
            noteList.remove(dataSnapshot?.getValue(Note::class.java))
            adapter.notifyDataSetChanged()
        }, {
// onChildChanged, TODO: update the change in elements value in noteList
            dataSnapshot, s ->

        })

    }

    fun editNote(note: Note) {
        val i = Intent(this, EditNoteActivity::class.java)
        i.putExtra(EditNoteActivity.EXTRA_NOTE, note)
        startActivity(i)
    }
}

PS: addChildListener is an extension function. (check comments in code for the name of callbacks)
Since dataSnapshot can only tell us the key and value that was changed (as far as I know from the docs), how will I update noteList without knowing the index of the changed element? Or is there really a way to get the index?

A:

I would by far recommend that you use FirebaseUI which is built exactly for your use case.
However, if you'd really like to go solo, then no, the only way to get the index of an updated child is by finding it in your existing data. Here's an example from FirebaseUI's internals:
private int getIndexForKey(String key) {
    int index = 0;
    for (DataSnapshot snapshot : mSnapshots) {
        if (snapshot.getKey().equals(key)) {
            return index;
        } else {
            index++;
        }
    }
    throw new IllegalArgumentException("Key not found");
}

Here's how FUI uses getIndexForKey(String).
Iterating through the list works just fine in terms of performance as long as you aren't downloading ridiculously large lists. Otherwise, you can use a Map<String, Integer> and see the attempt for FUI here.
PS: your onChildAdded code is wrong for inserts (items will always be added at the end even if they are in the middle). See how FUI does this.

