Building Java Programs Chapter 15 Implementing a Collection
Building Java Programs Chapter 15 Implementing a Collection Class: Array. Int. List
Exercise • Write a program that reads a file (of unknown size) full of integers and prints the integers in the reverse order to how they occurred in the file. Consider example file data. txt: 17 932085 -32053278 100 3 – When run with this file, your program's output would be: 3 100 -32053278 932085 17 2
Solution using arrays int[] nums = new int[100]; int size = 0; // make a really big array Scanner input = new Scanner(new File("data. txt")); while (input. has. Next. Int()) { nums[size] = input. next. Int(); // read each number size++; // into the array } for (int i = size - 1; i >= 0; i--) { System. out. println(nums[i]); // print reversed } index 0 value 17 932085 -32053278 100 size 1 2 3 4 5 6 . . . 98 99 3 0 0 . . . 0 0 5 3
Unfilled arrays int[] nums = new int[100]; int size = 0; • We often need to store an unknown number of values. – Arrays can be used for this, but we must count the values. – Only the values at indexes [0, size - 1] are relevant. • We are using an array to store a list of values. – What other operations might we want to run on lists of values? index 0 1 2 3 value 17 932085 -32053278 100 size 5 4 5 6 . . . 98 99 3 0 0 . . . 0 0 4
Other possible operations public. . . static void add(int[] list, int size, int value, int index) remove(int[] list, int size, int index) find(int[] list, int size, int value) print(int[] list, int size) • We could implement these operations as methods that accept a list array and its size along with other parameters. – But since the behavior and data are so closely related, it makes more sense to put them together into an object. – A list object can store an array of elements and a size, and can have methods for manipulating the list of elements. • Promotes abstraction (hides details of how the list works) 5
Exercise • Let's write a class that implements a list using an int[] – We'll call it Array. Int. List – behavior: • add(value), add(index, value) • get(index), set(index, value) • size() • remove(index) • index. Of(value) • to. String(). . . – The list's size will be the number of elements added to it so far. – How will the list be used? . . . 6
Implementing add • How do we add to the end of a list? index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. add(42); index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 42 0 0 0 7 7
Implementing add, cont. • To add to end of list, just store element and increase size: public void add(int value) { list[size] = value; size++; } index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. add(42); index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 42 0 0 0 7 8
Implementing add (2) • How do we add to the middle or end of the list? index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. add(3, 42); index value size // insert 42 at index 3 0 1 2 3 4 5 6 7 8 9 3 8 9 42 7 5 12 0 0 0 7 9
Implementing add (2) cont. • Adding to the middle or front is hard (see book ch 7. 3) – must shift nearby elements to make room for the new value index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. add(3, 42); index value size // insert 42 at index 3 0 1 2 3 4 5 6 7 8 9 3 8 9 42 7 5 12 0 0 0 7 – Note: The order in which you traverse the array matters! 10
Implementing add (2) code public void add(int index, int value) { for (int i = size; i > index; i--) { list[i] = list[i - 1]; } list[index] = value; } index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. add(3, 42); index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 42 7 5 12 0 0 0 7 11
Other methods • Let's implement the following methods next: – – size get set to. String 12
Implementing remove • How can we remove an element from the list? index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. remove(2); index value size // delete 9 from index 2 0 1 2 3 4 5 6 7 8 9 3 8 7 5 12 0 0 0 5 13
Implementing remove, cont. • Again, we need to shift elements in the array – this time, it's a left-shift – in what order should we process the elements? – what indexes should we process? index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. remove(2); index value size // delete 9 from index 2 0 1 2 3 4 5 6 7 8 9 3 8 7 5 12 0 0 0 5 14
Implementing remove code public void remove(int index) { for (int i = index; i < size; i++) { list[i] = list[i + 1]; } size--; list[size] = 0; // optional (why? ) } index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 0 0 6 – list. remove(2); index value size // delete 9 from index 2 0 1 2 3 4 5 6 7 8 9 3 8 7 5 12 0 0 0 5 15
Running out of space • What should we do if the client adds more than 10 elements? index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 12 4 8 1 6 10 – list. add(15); // add an 11 th element index 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 value 3 8 9 7 5 12 4 8 1 6 15 0 0 0 0 0 size 11 16
Convenience methods • Implement the following methods: – index. Of - returns the first index an element is found, or -1 if not – is. Empty - returns true if list has no elements – contains - returns true if the list contains the given int value • Why do we need is. Empty and contains when we already have index. Of and size ? – These methods provide convenience to the client of our class. if (my. List. size() == 0) { if (my. List. is. Empty()) { if (my. List. index. Of(42) >= 0) { if (my. List. contains(42)) { 17
More Array. Int. List • Let's add some new features to our Array. Int. List class: 1. A method that allows client programs to print a list's elements 2. A constructor that accepts an initial capacity (By writing these we will recall some features of objects in Java. ) • Printing lists: You may be tempted to write a print method: // client code Array. Int. List list = new Array. Int. List(); . . . list. print(); – Why is this a bad idea? What would be better? 18
The to. String method • Tells Java how to convert an object into a String Array. Int. List list = new Array. Int. List(); System. out. println("list is " + list); // ("list is " + list. to. String()); • Syntax: public String to. String() { code that returns a suitable String; } • Every class has a to. String, even if it isn't in your code. – The default is the class's name and a hex (base-16) number: Array. Int. List@9 e 8 c 34 19
to. String solution // Returns a String representation of the list. public String to. String() { if (size == 0) { return "[]"; } else { String result = "[" + element. Data[0]; for (int i = 1; i < size; i++) { result += ", " + element. Data[i]; } result += "]"; return result; } } 20
Multiple constructors • existing constructor: public Array. Int. List() { element. Data = new int[10]; size = 0; } • Add a new constructor that accepts a capacity parameter: public Array. Int. List(int capacity) { element. Data = new int[capacity]; size = 0; } – The constructors are very similar. Can we avoid redundancy? 21
this keyword • this : A reference to the implicit parameter (the object on which a method/constructor is called) • Syntax: – To refer to a field: this. field – To call a method: this. method(parameters); – To call a constructor this(parameters); from another constructor: 22
Revised constructors public Array. Int. List(int capacity) { element. Data = new int[capacity]; size = 0; } public Array. Int. List() { this(10); // calls (int) constructor } 23
Size vs. capacity • What happens if the client tries to access an element that is past the size but within the capacity (bounds) of the array? – Example: list. get(7); on a list of size 5 (capacity 10) index value size 0 1 2 3 4 5 6 7 8 9 3 8 9 7 5 0 0 0 5 – Answer: Currently the list allows this and returns 0. • Is this good or bad? What (if anything) should we do about it? 24
Preconditions • precondition: Something your method assumes is true at the start of its execution. – Often documented as a comment on the method's header: // Returns the element at the given index. // Precondition: 0 <= index < size public void remove(int index) { return element. Data[index]; } – Stating a precondition doesn't really "solve" the problem, but it at least documents our decision and warns the client what not to do. – What if we want to actually enforce the precondition? 25
Bad precondition test • What is wrong with the following way to handle violations? // Returns the element at the given index. // Precondition: 0 <= index < size public void remove(int index) { if (index < 0 || index >= size) { System. out. println("Bad index! " + index); return -1; } return element. Data[index]; } – returning -1 is no better than returning 0 (could be a legal value) – println is not a very strong deterrent to the client (esp. GUI) 26
Throwing exceptions (4. 5) throw new Exception. Type(); throw new Exception. Type("message"); • Causes the program to immediately crash with an exception. • Common exception types: – Arithmetic. Exception, Array. Index. Out. Of. Bounds. Exception, File. Not. Found. Exception, Illegal. Argument. Exception, Illegal. State. Exception, IOException, No. Such. Element. Exception, Null. Pointer. Exception, Runtime. Exception, Unsupported. Operation. Exception • Why would anyone ever want the program to crash? 27
Exception example public void get(int index) { if (index < 0 || index >= size) { throw new Array. Index. Out. Of. Bounds. Exception(index); } return element. Data[index]; } – Exercise: Modify the rest of Array. Int. List to state preconditions and throw exceptions as appropriate. 28
Postconditions • postcondition: Something your method promises will be true at the end of its execution. – Often documented as a comment on the method's header: // Makes sure that this list's internal array is large // enough to store the given number of elements. // Postcondition: element. Data. length >= capacity public void ensure. Capacity(int capacity) { // double in size until large enough while (capacity > element. Data. length) { element. Data = Arrays. copy. Of(element. Data, 2 * element. Data. length); } } – If your method states a postcondition, clients should be able to rely on that statement being true after they call the method. 29
Writing testing programs • Some programs are written specifically to test other programs. • If we wrote Array. Int. List and want to give it to others, we must make sure it works adequately well first. • Write a client program with a main method that constructs several lists, adds elements to them, and calls the various other methods. 30
Tips for testing • You cannot test every possible input, parameter value, etc. – Even a single (int) method has 2^32 different possible values! – So you must think of a limited set of tests likely to expose bugs. • Think about boundary cases – positive, zero, negative numbers – right at the edge of an array or collection's size • Think about empty cases and error cases – 0, -1, null; an empty list or array – an array or collection that contains null elements • Write helping methods in your test program to shorten it. 31
More testing tips • Focus on expected vs. actual behavior • the test shouldn't just call methods and print results; it should: – – call the method(s) compare their results to a known correct expected value if they are the same, report that the test "passed" if they differ, report that the test "failed" along with the values • test behavior in combination – maybe add usually works, but fails after you call remove – what happens if I call add then size? remove then to. String? – make multiple calls; maybe size fails the second time only 32
Example Array. Int. List test public static void main(String[] args) { int[] a 1 = {5, 2, 7, 8, 4}; int[] a 2 = {2, 7, 42, 8}; int[] a 3 = {7, 42}; helper(a 1, a 2); helper(a 2, a 3); helper(new int[] {1, 2, 3, 4, 5}, new int[] {2, 3, 42, 4}); } public static void helper(int[] elements, int[] expected) { Array. Int. List list = new Array. Int. List(elements); for (int i = 0; i < elements. length; i++) { list. add(elements[i]; } list. remove(0); list. remove(list. size() - 1); list. add(2, 42); for (int i = 0; i < expected. length; i++) { if (list. get(i) != expected[i]) { System. out. println("fail; expect " + Arrays. to. String(expected) + ", actual " + list); } } } 33
Finishing Array. Int. List • Let's add the following features to Array. Int. List: – a constant for the default list capacity – better encapsulation and protection of implementation details – a better way to print list objects 34
Class constants public static final type name = value; • class constant: a global, unchangeable value in a class – used to store and give names to important values used in code – documents an important value; easier to find and change later • classes will often store constants related to that type – Math. PI – Integer. MAX_VALUE, Integer. MIN_VALUE – Color. GREEN // default array length for new Array. Int. Lists public static final int DEFAULT_CAPACITY = 10; 35
"Helper" methods • Currently our list class has a few useful "helper" methods: – public void check. Resize() – public void check. Index(int index, int min, int max) • We wrote them to help us implement other required methods. • We don't want clients to call these methods; they are internal. – How can we stop clients from calling them? 36
A private method private type name(type name, . . . , type name) { statement(s); } • a private method can be seen/called only by its own class – encapsulated, similar to fields – your object can call the method on itself, but clients cannot call it – useful for "helper" methods that clients shouldn't directly touch private void check. Index(int index, int min, int max) { if (index < min || index > max) { throw new Index. Out. Of. Bounds. Exception(index); } } 37
Printing an Array. Int. List • Currently our list class has a print method: // client code Array. Int. List list = new Array. Int. List(); . . . list. print(); – Why is this a bad idea? What would be better? 38
The to. String method • Tells Java how to convert an object into a String Array. Int. List list = new Array. Int. List(); System. out. println("list is " + list); • Syntax: public String to. String() { code that returns a suitable String; } • Every class has a to. String, even if it isn't in your code. – The default is the class's name and a hex (base-16) number: Array. Int. List@9 e 8 c 34 39
to. String solution // Returns a String representation of the list. public String to. String() { if (size == 0) { return "[]"; } else { String result = "[" + element. Data[0]; for (int i = 1; i < size; i++) { result += ", " + element. Data[i]; } result += "]"; return result; } } 40
Exercise • Write a class called Stutter. Int. List. – Its constructor accepts an integer stretch parameter. – Every time an integer is added, the list will actually add stretch number of copies of that integer. • Example usage: Stutter. Int. List list = new Stutter. Int. List(3); list. add(7); // [7, 7, 7] list. add(-1); // [7, 7, 7, -1, -1] list. add(2, 5); // [7, 7, 5, 5, 5, 7, -1, -1] list. remove(4); // [7, 7, 5, 5, 7, -1, -1] System. out. println(list. get. Stretch()); // 3 41
Inheritance • inheritance: Forming new classes based on existing ones. – a way to share/reuse code between two or more classes – superclass: Parent class being extended. – subclass: Child class that inherits behavior from superclass. • gets a copy of every field and method from superclass 42
An Employee class public class Employee {. . . public int get. Hours() { return 40; } // works 40 hours / week public double get. Salary() { return 40000. 0; // $40, 000. 00 / year } public int get. Vacation. Days() { return 10; // 2 weeks' paid vacation } } public String get. Vacation. Form() { return "yellow"; // use the yellow form } • Lawyers, Secretaries, etc. have similar behavior to the above. • How to implement those classes without redundancy? 43
Inheritance syntax public class name extends superclass { – Example: public class Lawyer extends Employee {. . . } • By extending Employee, each Lawyer object now: – receives a copy of each method from Employee automatically – can be treated as an Employee by client code 44
Overriding methods • override: To replace a superclass's method by writing a new version of that method in a subclass. – No special syntax is required to override a method. Just write a new version of it in the subclass. public class Lawyer extends Employee { // overrides get. Salary method in Employee class; // give Lawyers a $5 K raise public double get. Salary() { return 45000. 00; } } 45
super keyword • Subclasses can call overridden methods with super. method(parameters) – Example: public class Lawyer extends Employee { // give Lawyers a $5 K raise (better) public double get. Salary() { double base. Salary = super. get. Salary(); return base. Salary + 5000. 00; } } – This version makes sure that Lawyers always make $5 K more than Employees, even if the Employee's salary changes. 46
Calling super constructor super(parameters); – Example: public class Lawyer extends Employee { public Lawyer(String name) { super(name); // calls Employee constructor }. . . } – super allows a subclass constructor to call a superclass one. – The super call must be the first statement in the constructor. – Constructors are not inherited; If you extend a class, you must write all the constructors you want your subclass to have. 47
Exercise solution public class Stutter. Int. List extends Array. Int. List { private int stretch; public Stutter. Int. List(int stretch. Factor) { super(); stretch = stretch. Factor; } public Stutter. Int. List(int stretch. Factor, int capacity) { super(capacity); stretch = stretch. Factor; } public void add(int value) { for (int i = 1; i <= stretch; i++) { super. add(value); } } public void add(int index, int value) { for (int i = 1; i <= stretch; i++) { super. add(index, value); } } } public int get. Stretch() { return stretch; } 48
Subclasses and fields public class Employee { private double salary; . . . } public class Lawyer extends Employee {. . . public void give. Raise(double amount) { salary += amount; // error; salary is private } } • Inherited private fields/methods cannot be directly accessed by subclasses. (The subclass has the field, but it can't touch it. ) – How can we allow a subclass to access/modify these fields? 49
Protected fields/methods protected type name; // field protected type name(type name, . . . , type name) { statement(s); // method } • a protected field or method can be seen/called only by: – the class itself, and its subclasses – also by other classes in the same "package" (discussed later) – useful for allowing selective access to inner class implementation public class Employee { protected double salary; . . . } 50
Our list classes • We implemented the following two list classes: – Array. Int. List index 0 1 2 value 42 -3 17 – Linked. Int. List data next front 42 data next -3 data next 17 – Problem: • We should be able to treat both lists the same way in client code. 51
Recall: ADT interfaces (11. 1) • abstract data type (ADT): A specification of a collection of data and the operations that can be performed on it. – Describes what a collection does, not how it does it. • Java's collection framework describes ADTs with interfaces: – Collection, Deque, List, Map, Queue, Set, Sorted. Map • An ADT can be implemented in multiple ways by classes: – Array. List and Linked. List implement List – Hash. Set and Tree. Set implement Set – Linked. List , Array. Deque, etc. implement Queue • Exercise: Create an ADT interface for the two list classes. 52
An Int. List interface (16. 4) // Represents a list of integers. public interface Int. List { public void add(int value); public void add(int index, int value); public int get(int index); public int index. Of(int value); public boolean is. Empty(); public void remove(int index); public void set(int index, int value); public int size(); } public class Array. Int. List implements Int. List {. . . public class Linked. Int. List implements Int. List {. . . 53
Our list classes • We have implemented the following two list collection classes: – Array. Int. List index 0 1 2 value 42 -3 17 – Linked. Int. List data next front 42 data next -3 data next 17 – Problem: • They can store only int elements, not any type of value. 54
Type Parameters (Generics) Array. List<Type> name = new Array. List<Type>(); • Recall: When constructing a java. util. Array. List, you specify the type of elements it will contain between < and >. – We say that the Array. List class accepts a type parameter, or that it is a generic class. Array. List<String> names = new Array. List<String>(); names. add("Marty Stepp"); names. add("Stuart Reges"); 55
Implementing generics // a parameterized (generic) class public class name<Type> {. . . } – By putting the Type in < >, you are demanding that any client that constructs your object must supply a type parameter. • You can require multiple type parameters separated by commas. – The rest of your class's code can refer to that type by name. • Exercise: Convert our list classes to use generics. 56
Generics and arrays public class Foo<T> { private T my. Field; public void method 1(T param) { my. Field = new T(); T[] a = new T[10]; } (15. 4) // ok // error } – You cannot create objects or arrays of a parameterized type. 57
Generics/arrays, fixed public class Foo<T> { private T my. Field; public void method 1(T param) { my. Field = param; T[] a 2 = (T[]) (new Object[10]); } // ok } – But you can create variables of that type, accept them as parameters, return them, or create arrays by casting Object[]. 58
Comparing generic objects public class Array. List<E> {. . . public int index. Of(E value) { for (int i = 0; i < size; i++) { // if (element. Data[i] == value) { if (element. Data[i]. equals(value)) { return i; } } return -1; } } – When testing objects of type E for equality, must use equals 59
Generic linked list nodes public class List. Node<E> { public E data; public List. Node<E> next; . . . } – For a generic linked list, the node class must also accept the type parameter E 60
Generic interface (15. 3, 16. 5) // Represents a list of values. public interface List<E> { public void add(E value); public void add(int index, E value); public E get(int index); public int index. Of(E value); public boolean is. Empty(); public void remove(int index); public void set(int index, E value); public int size(); } public class Array. Int. List<E> implements Int. List<E> {. . . public class Linked. Int. List<E> implements Int. List<E> {. . . 61
- Slides: 61