Friday, March 6, 2009
ENCAPSULATED LISTS (a case study in encapsulation)
Homeworks 3, 4, and 5 introduced you to three different implementations of
linked lists, each fundamentally different.
With the Homework 3 lists, if an application writer wants to query the identity
of every item in the list without modifying the list, it takes time
proportional to the square of n, the number of items in the list (i.e.,
Theta(n^2) time), because you have to use nth(i) to identify each item in time
proportional to i.
The lists in Homeworks 4 and 5 allow an application to directly hold a node in
By alternating between the next() method and the item field or method,
you can query all the list’s items in Theta(n) time.
Similarly, if an
application holds a node in the middle of a list, it can insert or delete c
items there in time proportional to c, no matter how long the list is.
The Homework 5 lists (SList and DList) are well-encapsulated, whereas the
Homework 4 DList has flaws.
I will discuss these flaws today to illustrate why
designing the really good list ADTs of Homework 5 was tricky.
Let’s ask some
questions about how lists should behave.
What happens if we invoke l.remove(n)--but the node n is in a different
list than l?
In Homework 4, Part II asks whether it is possible for an application to
break the DList invariants.
One way to do this is to mismatch nodes and
lists in method calls.
When an application does this, the "size" field of
the wrong list is updated, thereby breaking the invariant that a list’s
size field should be correct.
How can we fix this?
ADT interface answer:
The methods remove(), insertAfter(), etc. should
always update the right list’s "size" field.
It’s unacceptably slow to walk through a whole
list just to see if the node n is really in the list l.
node should keep a reference to the list that contains it.
In Homework 5,
each ListNode has a "myList" field.
Should insertAfter(), remove(), etc. be methods of List or ListNode?
Normally, we expect the methods that modify a data structure (like a List)