I would recommend completing the module [Apex Basics and Database](https://trailhead.salesforce.com/en/content/learn/modules/apex_database) on Trailhead. Two of the units in that module cover how to query and access data across relationships.

However, in this case, you don't need to traverse a relationship at all.

    appointment.Doctor__c.Id

should simply be replaced by

    appointment.Doctor__c

The relationship field itself contains the related record Id.

--- 

This code is at risk of a number of governor limits problems (in particular the [Visualforce view state limit](https://developer.salesforce.com/docs/atlas.en-us.salesforce_visualforce_best_practices.meta/salesforce_visualforce_best_practices/pages_best_practices_perf_code_view_state.htm)), as well as general poor performance, due to:

- executing and storing unfiltered queries, which return all records in the database.
- performing filtering in Apex instead of appropriately filtering SOQL queries to show relevant data.

I would recommend you consider removing your unfiltered queries and rebuilding `updateDoctor()` to perform a new SOQL query when the selected doctor changes to acquire the doctor record and their appointments, using a parent-child relationship query (covered in the module linked above). You would then be able to remove `updateAppointments()` entirely.