Title: Chapter 12 : Lists
1Chapter 12 Lists
2Objectives
- After studying this chapter you should understand
- the notion of container and its features
- the specification of a list as a container
- iteration as the control mechanism to process
list entries - the notion of equality, and the pitfalls in
overriding the method equals. - Also, you should be able to
- use the class DefaultListltElementgt to create and
manipulate list instances - implement iterative algorithms that manipulate
lists.
3Objectives
- Also, you should be able to
- use the class DefaultListltElementgt to create and
manipulate list instances - implement iterative algorithms that manipulate
lists.
4Containers for collections of objects
- Basic container operations include
- adding an item
- removing an item
- determining if a particular item is in container
- performing an operation on each item in the
container. - Fundamental container characteristic
- Container is a homogeneous collection
5List as container
- Finite container. It may be empty.
- Its a sequence.
- Its homogeneous. All the elements are of the
same type. - It can contain multiple instances of same item.
6Designing List StudentList
- public void add (Student student)
- Add the specified Student to the end of this
list. - public void remove (Student student)
- Remove the first occurrence of the specified
Student from this list. Has no effect if the
Student is not on this list. - public boolean contains (Student student)
- This list contains the specified Student.
- public int size ()
- Number of elements in this list.
- public Student get (int index)
- The Student with the specified index.
- require 0 lt index index lt this.size()
- public void set (int index, Student student)
- Replace the element at the specified position
with the specified Student. - require 0 lt index index lt this.size()
7Client of StudentList
- StudentList roll new StudentList()
- roll.add(new Student("Bill",)) // adding items
to roll - if (roll.size() lt 10) //querying its size
- Student s roll.get(3) // accessing a
Student, where roll.size()gt3 - // if finalGrade() is a Student method, we can
write - if (roll.get(0).finalGrade() gt 70)
8Specifying Lists
- To model a list of PlayingCards.
- a class has the same features as StudentList.
- methods are modified to reflect that list
elements are now instances of PlayingCard
9Client safety
- We can safely write code like
StudentList roll new StudentList() roll.add(new
Student("Bill",)) if (roll.get(0).finalGrade()
gt 70)
PlayingCardList hand new PlayingCardList() hand
.add(new PlayingCard(PlayingCard.SPADE,2)) if
(hand.get(0).suit() PlayingCard.SPADE)
10Client safety
- We expect the following to produce compiler
errors
- roll.add(new PlayingCard(PlayingCard.SPADE,2))
- (Cannot add a PlayingCard to a StudentList.)
- hand.get(0).finalGrade()
- (hand.get(0) is a PlayingCard, no finalGrade
method.)
11 at a cost Do not repeat yourself!
- Implementation of classes StudentList, and
PlayingCardList, will be virtually identical. - Client methods that manipulate StudentList
- public void swap (StudentList list, int i, int j)
- public void sort (StudentList list)
- public int search (Student s, StudentList list)
- Will need to be duplicated to deal with any other
XXXList type!
12Using abstraction to capture list-ness
- AbstractList specifies and implements all list
methods, subclasses will inherit the
implementations. - Items in AbstractList are specified of type Object
public void add (Object object) Add the specified
Object to this list. public Object get (int
index) The Object on this list with the specified
index.
13Subclassing AbstractList
- In subclass StudentList we re-implement the
methods dealing with Student instances
public Student get (int i) return
(Student)super.get(i) public void add (Object
student) assert student instanceof
Student super.add(student)
14Subclassing AbstractList breaks LSP
- Specification of add in AbstractList is
public void add (Object object) Add the specified
Object to this list.
15Subclassing AbstractList breaks LSP
- StudentList cannot be used where AbstractList is
required. - Broken code
public void addAnObject (AbstractList list)
list.add(new Object())
16Using generics to capture list-ness
- Share specification and implementation
- class definition specifies dummy Element type as
parameter - Element is used in List implementation
public class ListltElementgt public void
add (Element element) public Element get
(int index)
17Using generics to capture list-ness
- List clients provide item type at creation to
fill for Element -
ListltStudentgt roll new ListltStudentgt() Listlt
PlayingCardgt hand new ListltPlayingCardgt()
18List definition structure
19public interface ListltElementgt A finite list of
Elements
Queries public int size () Number of elements in
this List. ensure this.size() gt 0 public
boolean isEmpty () This List contains no
elements. this.isEmpty() (this.size()
0) public Element get (int index) The Element
with the specified index. require 0 lt index
index lt this.size()
20Queries (Cont.)
public boolean contains (Element element) This
List contains the specified Element. this.contain
s(element) (this.indexOf(element) gt
0) public int indexOf (Element element) The
index of the first occurrence of the specified
element, or -1 if this List does not contain the
specified element. ensure if this.indexOf(elemen
t) gt 0 this.get(this.indexOf(element)).equ
als (element) for all j 0 lt j j lt
this.indexOf(element) implies
!this.get(j).equals(element) if
this.indexOf(element) -1 for all indexes
j, !this.get(j).equals(element)
21Commands
public void add (Element element) Append the
specified Element to the end of this List.
Equivalent to this.add(this.size(),element). requ
ire element ! null ensure this.size()
old.size() 1 this.get(this.size()-1)
element public void add (int index, Element
element) Insert the specified Element at the
specified index. require element !
null 0 lt index index lt this.size() ensure
this.size() old.size()
1 this.get(index) element for all j index
lt j j lt old.size() implies old.get(j)
this.get(j1)
22Commands (Cont.)
public void remove (Element element) Remove the
first occurrence of the specified Element from
this List. Has no effect if the Element is not
contained in this List. public void remove (int
index) Remove the element with the specified
index. require 0 lt index index lt
this.size() ensure this.size() old.size()
- 1 for all j index lt j j lt
this.size() implies this.get(j)
old.get(j1) public void set (int index, Element
element) Replace the element at the specified
position with the specified Element. require
element ! null, 0 lt index index lt
this.size() public ListltElementgt copy () A copy
of this List. ensure this.copy() ! this,
this.copy().size() this.size() for
all indexes j, this.get(j).equals(this.copy().get(
j))
23List iteration
- To perform an operation for each element of
container.
while ( condition ) statement
24Loop structure to process all list elements
int i // index of next element to
process i 0 // starts with first list
element while ( i lt list.size() ) process
list.get(i) i i 1
25Iterating to compute final average
public double finalAverage (ListltStudentgt
students) int i // index of students int
length // number of Students on the list int
sum // sum of grades up to, but not
including, // the i-th Student sum
0 length students.size() i 0 while (i
lt length) sum sum students.get(i).finalExa
m() i i1 return (double)sum /
(double)length
26Execution of finalAverage() method
- Assume we have the following student list data
list.get(0).finalExam() 75 list.get(1).finalExa
m() 80 list.get(2).finalExam()
93 list.get(3).finalExam() 67
- Note that
- list.size() 4
- And list indices are 0, 1, 2, and 3
27Execution of finalAverage() method
Loop initialization variables yield
Loop condition (i lt length ) is true Loop body
executes sum sum list.get(0).finalGrade()
28Execution of finalAverage() method
And then executes i i 1
29Execution of finalAverage() method
Loop condition (i lt length ) is true Loop body
executes sum sum list.get(1).finalGrade()
30Execution of finalAverage() method
And then it executes i i 1
31Execution of finalAverage() method
Loop condition (i lt length ) is true Loop body
executes sum sum list.get(2).finalGrade()
32Execution of finalAverage() method
And then it executes i i 1
33Execution of finalAverage() method
Loop condition (i lt length ) is true Loop body
executes sum sum list.get(3).finalGrade()
34Execution of finalAverage() method
And then it executes i i 1
35Execution of finalAverage() method
Loop condition (i lt length ) is now false, Loop
terminates. Method proceeds to compute division
and return value.
36Finding the minimum grade
public int minFinalExam (ListltStudentgt
students) The lowest final exam grades of the
specified Students. require students.size() gt 0
- Check final exam grade of each Student, maintain
lowest seen.
int i // index of the next Student to see int
low // lowest final exam grade of Students //
seen so far
37Finding the minimum grade
int i // index of the next Student to see int
low // lowest final exam grade of Students //
seen so far
- To what values to initialize them?
- Initialize i to 0 implies that no student has
been seen, - So, what value to initialize low ?
38Finding the minimum grade
- Simplest solution Since list is not empty
low students.get(0).finalExam() i 1 //
this will be the next student to see.
- Loop loop checks and updates low if needed
while (i lt students.size()) if
(students.get(i).finalExam() lt low) low
students.get(i).finalExam() i i1 return
low
39Searching the list
- Implement indexOf using the other List methods.
public int indexOf (Element element) The index of
the first occurrence of the specified element,
or -1 if this List does not contain the
specified element.
- Use equals to compare list item to element
this.get(i).equals(element)
40Searching the list
- In search, iteration must stop as soon element
is found in list. - Search is on as long as item examined is not
equal to element. - If element not in list, make sure index does not
go past size of list.
int i 0 // index of next element to
examine while ( i lt this.size()
!this.get(i).equals(element))
- Note about conditional expression
- order of operands
- Expressions evaluated
41Searching the list Loop invariant
- i index of next element in list to examine.
- This is always true during execution
- No list item with index less than i is equal to
element
42Searching the list
- What about body of loop?
- Entering body of loop implies that current item
is not equal to element, thus try next index.
int i 0 // index of the next element to
examine while (i lt this.size()
!this.get(i).equals(element)) i i 1
body of loop
43Searching the list
i this.size() //thus item not in list
this.get(i).equals(element) //thus, item found
- Either condition may be true after loop
terminates. - Must check for them to return appropriate value.
44Searching the list
public int indexOf (Element element) int i
0 // index of next element to examine while
(i lt this.size() !this.get(i).equals(eleme
nt)) i i1 if (i lt this.size()) return
i else return -1
45Nested loops Removing repeated list items.
- Problem Remove repeated entries from a list of
Integer values. - Assume list is in variable elements.
private removeDuplicates () Remove duplicates
from the elements list
46Nested loops Removing repeated list items
- Design
- For each list item, iterate through list removing
all repetitions. - Loop invariant
- No element with index less than i appears more
than once
47Nested loops Removing duplicate list items
48Nested loops Removing duplicate list items
- Need loop to remove entries in list equals to
list.get(i) - can be written directly as a nested loop.
- can write a private method.
- Prefer to write a method to nest loops
- Easier to write, read and understand.
- Easier to test.
private void removeDuplicates () int i
0 while (i lt elements.size())
removeDuplicatesOfItemAt(i) i i 1
49Removing duplicate list items
private void removeDuplicatesOfItemAt (int i)
int j // index of element to
check Integer item // remove duplicates of
this item elements.get(i) j i1 while (j
lt elements.size()) if (item.equals(elements.ge
t(j))) elements.remove(j) else j
j1
50Nested loops Removing duplicate list items
- Replacing invocation of removeDuplicatesOfItemAt
with its body in removeDuplicates, resulting
structure shows one while loop inside body of
another loop nested loops
i 0 while (i lt elements.size()) Integer
item elements.get(i) int j i1 while (j lt
elements.size()) if (item.equals(elements.get(j)
)) elements.remove(j) else j j1 i
i1
51Loop structure summary
initialization while (condition)
body conclusion
- Initialization statements initialize variables
used in the condition and in the body of the
loop. Critical part of setting up loop. - Condition determines if body of the loop is to be
executed. - Loop body defines one step in the solution
processmust make sure that loop condition will
ultimately become false, and loop terminates. - Concluding statements are not always necessary.
52The for statement
for (initializationStatement condition
updateStatement) bodyStatement
initializationStatement while (condition)
bodyStatement updateStatement
53The for statement
- A for-loop iteration processing each element of a
list
int i for (i 0 i lt list.size() i
i1) process list.get(i)
54for statement example
- Method average written with a for-loop
for (i 0 i lt length i i1) if
(students.get(i).finalExam() gt 0) sum
sum students.get(i).finalExam() count
count1
55What does equal mean
- Operator
- compares two values for equality.
- Two reference values are equal when they
reference the same object.
Student s1 Student s2 s1 s2 is true
only if s1 and s2 refer to exactly the same
object.
56What does equal mean
- In the following situation
Student s1 new (John Doe, 12456,
.) Student s2 s1 Student s3 new (John
Doe, 12456, ) s1 s2 //will return true,
while s1 s3 //will return false.
57Object equals
- Every class inherits method equals from Object.
- In Object, equals is by default defined using
public boolean equals (Object obj) return this
obj
- If equal is not overridden, s1.equals(s2) means
s1 s2. - They must be the same object to return true.
58When to override equals
- Often need to check if two different instances
are equal. - Using will yield false.
- Must use appropriate implementation of equals
- Consider overriding equals in classes whose
instances are immutable. - Think carefully to override equals in mutable
class - in this case use only class attributes which are
immutable.
59Overriding equals pitfalls
- Given the specification of the equals method
public boolean equals (Object obj)
- Cannot redefine parameter, it will only overload
it. - Overriding equals should be limited to top level
class in an inheritance hierarchy.
60Overriding equals
public class Circle extends ClosedFigure
public Circle (int radius) /
Two Circles are equal if and only if they have
the same radius. / public boolean equals
(Object obj) if (obj instanceof
Circle) return this.radius()
((Circle)obj).radius() else return
false
61Overriding equals
public class ColoredCircle extends Circle
public ColoredCircle (int radius, Color c)
/ Two ColoredCircles are equal if
and only if they have the same radius and
Color. / public boolean equals (Object obj)
if (obj instanceof ColoredCircle) return
super.equals(obj) this.color().equals(
((ColoredCircle)obj).color()) else return
false
62Problems with overriding
- Broken subtyping by weakening the postcondition
of Circles equals. The contract offered by
Circle says
if c is a Circle, this.equals(c)
(this.radius() c.radius()).
- ColoredCircle, however, promises only that
if c is a ColoredCircle, if this.equals(c), then
this.radius() c.radius().
63Problems with overriding
- A Circle client can see unequal Circles with same
radius - if two Circles happen to be ColoredCircles of
different colors, they test as unequal even if
the client is viewing them as Circles and not
ColoredCircles.
64Problems with overriding
public void report (Circle c1, Circle c2) if
(c1.radius() c2.radius())
System.out.println("Circles are equal.") if
(!c1.equals(c2)) System.out.println("Circles
are not equal.")
- Report that arguments are both equal and not
equal if invoked as
- report(new ColoredCircle(1,Color.BLUE), new
ColoredCircle(1,Color.RED))
65Equals properties are not met
- equals conditions
- reflexive x.equals(x) is true
- symmetric if x.equals(y) is true, then so is
y.equals(x). - transitive x.equals(y) and y.equals(z) then
x.equals(z). - Circle c1 new Circle(1)
- Circle c2 new ColoredCircle(1,Color.BLUE)
- c1.equals(c2) is true, but c2.equals(c1) is
false. ? - c1.equals(c2) compares them as Circle instances,
while c2.equals(c1) compares them as
ColoredCircle instances.
66Equals properties are not met
Circle c1 new Circle(1) Circle c2 new
ColoredCircle(1,Color.BLUE)
- c1.equals(c2) is true, but c2.equals(c1) is
false. ? - c1.equals(c2) compares them as Circle instances,
while c2.equals(c1) compares them as
ColoredCircle instances.
67A broken fix
public boolean equals (Object obj) if (obj
instanceof ColoredCircle) return
super.equals(obj) this.color().equals(
((ColoredCircle)obj).color()) else if (obj
instanceof Circle) return super.equals(obj)
else return false
68A broken fix
- Now if the argument is a Circle, but not a
ColoredCircle, the two objects will be compared
as Circles.
Circle c1 new Circle(1) Circle c2 new
ColoredCircle(1, Color.BLUE) both c1.equals(c2)
and c2.equals(c1) return true.
69A broken fix
report(new ColoredCircle(1, Color.BLUE)), new
ColoredCircle(1, Color.RED))
- Still reports that arguments are both equal and
not equal.
70A broken fix
Circle c1 new ColoredCircle(1,
Color.BLUE) Circle c2 new Circle(1) Circle c3
new ColoredCircle(1, Color.RED)
- Then
- c1.equals(c2) and c2.equals(c3) are both true,
- but c1.equals(c3) is false.
71Other attempts to fix equals
- Use the getClass() method, that returns the
run-time type of instances. - Define equals only if both arguments are of same
run-time - i. equals properties become true.
- ii. But subtyping is still broken. Two colored
circles seen as Circle may be equal but unequal
under ColoredCircle.
72Another attempt to fix equals
- Instead of subclassing use composition thus we
do not have to override.
73Simplest solution
- Override equals only ONCE in a class hierarchy.
- Programmers override equals and make the method
final, so that subclasses cannot override it.
74Implementing List AbstractList
- Use an abstract class as a basis for constructing
list implementations. - The abstract class defines a number of list
methods in terms of a few basic methods. - A concrete class, need only implement these.
75Methods implemented in AbstractList
public boolean isEmpty () return this.size()
0 public boolean contains (Element
element) return this.indexOf(element) !
-1 public void add (Element element)
this.add(this.size(),element) public void
set (int i, Element element) this.remove(i) t
his.add(i, element) public void remove
(Element element) int i this.indexOf(element)
if (i ! -1) this.remove(i)
76Methods implemented in AbstractList
public int indexOf (Element element) int i
0 // index of the next element to
examine while (i lt this.size()
!this.get(i).equals(element)) i
i1 if (i lt this.size()) return
i else return -1
77Methods implemented in AbstractList
- We also provide an implementation of toString
public String toString () String s "" int
n this.size() if (n gt 0) s s
this.get(0).toString() int index 1 while
(index lt n) s s ", "
this.get(index).toString() index
index1 s s "" return s
78Method to be implemented in subclasses
- This leaves only five methods undefined
public int size() public void get (int
index) public void add (int index, Element
element) public void remove (int index) public
ListltElementgt copy ()
79Implementing DefaultList
- java.util.VectorltElementgt provides functionality
required. - A Vector is a list-like container in which
elements are accessed by index. - But Vector interface is not the desired one.
- We adapt or wrap the existing class.
- Thus implementation of DefaultList will have a
Vector component
80Static diagram for DefaultList
interface
ListltElementgt
adaptee
81DefaultList implementation
public class DefaultListltElementgt extends
AbstractListltElementgt private
java.util.VectorltElementgt elements public
DefaultList () this.elements new
java.util.vectorltElementgt() public int size
() return this.elements.size() public
Element get (int index) return
this.elements.get(index)
82DefaultList implementation
public void add (int index, Element element)
this.elements.add(index,element) public
void remove (int index) this.elements.removeEle
mentAt(index) public void set (int index,
Element element) this.elements.setElementAt(ele
ment,index)
83Summary
- examined specification and application of a
simple container called a list. - A list is a finite sequence of objects, all of
the same type. - An individual element on the list can be accessed
by an integer index, denoting how far the element
is from the front of the list. - List classes are characterized by the kind of
items they can contain. Thus one class models a
list of Students, while another class models a
list of PlayingCards. The specifications and
implementations of these different lists classes
are almost identical. To avoid having to repeat
the same code over and over for each different
list class, Java provides a generic facility.
This allows us to define interfaces, classes, and
methods with type parameters. Thus we define
the interface ListltElementgt where Element is a
type parameter. We instantiate the generic by
providing an actual type argument
ListltStudentgt is a list of Students, ListltStringgt
a list of Strings, and so on.
84Summary
- List classes are characterized by the kind of
items they can contain. - one class models a list of Students,
- while another class models a list of
PlayingCards. - Specifications and implementations of these
different lists classes are almost identical. - To avoid having to repeat same code over and over
for each different list class, Java provides a
generic facility.
85Summary
- Javas generic facility allows to define
interfaces, classes, and methods with type
parameters. - Define interface ListltElementgt where Element is a
type parameter. - instantiate the generic by providing an actual
type argument - ListltStudentgt is a list of Students,
- ListltStringgt a list of Strings, and so on.
86Summary
- Often want to perform some operation on each
element in a container. - Standard pattern is to use a while loop and index
to iterate through the list
int i i 0 while (i lt list.size()) process
list.get(i) i i 1
87Summary
- To search a container for an element must have an
exact idea of what it means for two objects to be
equal. - For mutable objects, equal generally means
identical. - With immutable objects, it is often the case that
several distinct object can represent the same
thing. - two different Strings might contain the same
sequence of characters, - two different Dates might denote the same actual
date. - Might want to override the inherited method
equals so that distinct objects that represent
the same thing compare as equal.
88Summary
- Must be very careful overriding equals
- Method equals should satisfy the reflexive,
symmetric, and transitive properties. - It is difficult to satisfy these properties if
equals is overridden more than once in a chain of
subclasses.
89Summary
- ListltElementgt is defined as a generic interface
that captures the functionality of a sequential
container. - Defined an abstract class AbstractListltElementgt
that implements most of the functionality and
provides a basis for developing complete concrete
implementations. - DefaultListltElementgt, wraps the standard library
class, java.util.VectorltElementgt.