0% found this document useful (0 votes)
69 views61 pages

Building Java Programs: Implementing A Collection Class: Arrayintlist

The document describes implementing a collection class called ArrayIntList that represents a list of integers stored in an array. It discusses methods for adding, removing, and accessing elements in the list. It also covers shifting elements when inserting or removing in the middle of the list and running out of space in the fixed-size array.

Uploaded by

Nora Masoud
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
69 views61 pages

Building Java Programs: Implementing A Collection Class: Arrayintlist

The document describes implementing a collection class called ArrayIntList that represents a list of integers stored in an array. It discusses methods for adding, removing, and accessing elements in the list. It also covers shifting elements when inserting or removing in the middle of the list and running out of space in the fixed-size array.

Uploaded by

Nora Masoud
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 61

Building Java Programs

Chapter 15
Implementing a Collection Class:
ArrayIntList

Copyright (c) Pearson 2013.


All rights reserved.
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]; // make a really big array
int size = 0;

Scanner input = new Scanner(new File("data.txt"));


while (input.hasNextInt()) {
nums[size] = input.nextInt(); // read each number
size++; // into the array
}

for (int i = size - 1; i >= 0; i--) {


System.out.println(nums[i]); // print reversed
}

index 0 1 2 3 4 5 6 ... 98 99
value 17 932085 - 100 3 0 0 ... 0 0
32053278
size 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 4 5 6 ... 98 99
value 17 932085 - 100 3 0 0 ... 0 0
32053278
size 5
4
Other possible operations
public static void add(int[] list, int size, int value, int index)
public static void remove(int[] list, int size, int index)
public static void find(int[] list, int size, int value)
public static void 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 ArrayIntList
– behavior:
• add(value), add(index, value)
• get(index), set(index, value)
• size()
• remove(index)
• indexOf(value)
• toString()
...

– 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 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.add(42);
index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 42 0 0 0
size 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 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.add(42);
index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 42 0 0 0
size 7
8
Implementing add (2)
• How do we add to the middle or end of the list?

index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.add(3, 42); // insert 42 at index 3

index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 42 7 5 12 0 0 0
size 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 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.add(3, 42); // insert 42 at index 3

index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 42 7 5 12 0 0 0
size 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 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.add(3, 42);
index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 42 7 5 12 0 0 0
size 7

11
Other methods
• Let's implement the following methods next:
– size
– get
– set
– toString

12
Implementing remove
• How can we remove an element from the list?

index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.remove(2);
index 0 1 2 //
3 delete
4 5 96 from
7 index
8 9 2
value 3 8 7 5 12 0 0 0 0 0
size 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 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.remove(2); // delete 9 from index 2


index 0 1 2 3 4 5 6 7 8 9
value 3 8 7 5 12 0 0 0 0 0
size 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 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 0 0 0 0
size 6

– list.remove(2);
index 0 1 2 //
3 delete
4 5 96 from
7 index
8 9 2
value 3 8 7 5 12 0 0 0 0 0
size 5

15
Running out of space
• What should we do if the client adds more than 10 elements?

index 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 12 4 8 1 6
size 10

– list.add(15); // add an 11th 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 0 0 0 0
size 11

16
Convenience methods
• Implement the following methods:
– indexOf - returns the first index an element is found, or -1 if not
– isEmpty - returns true if list has no elements
– contains - returns true if the list contains the given int value

• Why do we need isEmpty and contains when we already


have indexOf and size ?
– These methods provide convenience to the client of our class.

if (myList.size() == 0) { if (myList.isEmpty()) {

if (myList.indexOf(42) >= 0) { if (myList.contains(42)) {

17
More ArrayIntList
• Let's add some new features to our ArrayIntList 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
ArrayIntList list = new ArrayIntList();
...
list.print();
– Why is this a bad idea? What would be better?

18
The toString method
• Tells Java how to convert an object into a String
ArrayIntList list = new ArrayIntList();
System.out.println("list is " + list);
// ("list is " + list.toString());

• Syntax:
public String toString() {
code that returns a suitable String;
}

• Every class has a toString, even if it isn't in your code.


– The default is the class's name and a hex (base-16) number:
ArrayIntList@9e8c34
19
toString solution
// Returns a String representation of the list.
public String toString() {
if (size == 0) {
return "[]";
} else {
String result = "[" + elementData[0];
for (int i = 1; i < size; i++) {
result += ", " + elementData[i];
}
result += "]";
return result;
}
}

20
Multiple constructors
• existing constructor:
public ArrayIntList() {
elementData = new int[10];
size = 0;
}

• Add a new constructor that accepts a capacity parameter:


public ArrayIntList(int capacity) {
elementData = 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 ArrayIntList(int capacity) {


elementData = new int[capacity];
size = 0;
}

public ArrayIntList() {
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 0 1 2 3 4 5 6 7 8 9
value 3 8 9 7 5 0 0 0 0 0
size 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 elementData[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 elementData[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 ExceptionType();
throw new ExceptionType("message");

• Causes the program to immediately crash with an exception.

• Common exception types:


– ArithmeticException, ArrayIndexOutOfBoundsException, FileNotFoundException,
IllegalArgumentException, IllegalStateException, IOException, NoSuchElementException,
NullPointerException, RuntimeException, UnsupportedOperationException

• Why would anyone ever want the program to crash?

27
Exception example
public void get(int index) {
if (index < 0 || index >= size) {
throw new ArrayIndexOutOfBoundsException(index);
}
return elementData[index];
}

– Exercise: Modify the rest of ArrayIntList 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: elementData.length >= capacity
public void ensureCapacity(int capacity) {
// double in size until large enough
while (capacity > elementData.length) {
elementData = Arrays.copyOf(elementData,
2 * elementData.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 ArrayIntList 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 toString?
– make multiple calls; maybe size fails the second time only
32
Example ArrayIntList test
public static void main(String[] args) {
int[] a1 = {5, 2, 7, 8, 4};
int[] a2 = {2, 7, 42, 8};
int[] a3 = {7, 42, 42};
helper(a1, a2);
helper(a2, a3);
helper(new int[] {1, 2, 3, 4, 5}, new int[] {2, 3, 42, 4});
}

public static void helper(int[] elements, int[] expected) {


ArrayIntList list = new ArrayIntList(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.toString(expected)
+ ", actual " + list);
}
}
}
33
Finishing ArrayIntList
• Let's add the following features to ArrayIntList:
– 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 ArrayIntLists


public static final int DEFAULT_CAPACITY = 10;
35
"Helper" methods
• Currently our list class has a few useful "helper" methods:

– public void checkResize()


– public void checkIndex(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 checkIndex(int index, int min, int max) {


if (index < min || index > max) {
throw new IndexOutOfBoundsException(index);
}
}
37
Printing an ArrayIntList
• Currently our list class has a print method:

// client code
ArrayIntList list = new ArrayIntList();
...
list.print();

– Why is this a bad idea? What would be better?

38
The toString method
• Tells Java how to convert an object into a String
ArrayIntList list = new ArrayIntList();
System.out.println("list is " + list);

• Syntax:
public String toString() {
code that returns a suitable String;
}

• Every class has a toString, even if it isn't in your code.


– The default is the class's name and a hex (base-16) number:
ArrayIntList@9e8c34
39
toString solution
// Returns a String representation of the list.
public String toString() {
if (size == 0) {
return "[]";
} else {
String result = "[" + elementData[0];
for (int i = 1; i < size; i++) {
result += ", " + elementData[i];
}
result += "]";
return result;
}
}

40
Exercise
• Write a class called StutterIntList.
– 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:
StutterIntList list = new StutterIntList(3);
list.add(7); // [7, 7, 7]
list.add(-1); // [7, 7, 7, -1, -1, -1]
list.add(2, 5); // [7, 7, 5, 5, 5, 7, -1, -1, -1]
list.remove(4); // [7, 7, 5, 5, 7, -1, -1, -1]
System.out.println(list.getStretch()); // 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 getHours() {
return 40; // works 40 hours / week
}
public double getSalary() {
return 40000.0; // $40,000.00 / year
}
public int getVacationDays() {
return 10; // 2 weeks' paid vacation
}
public String getVacationForm() {
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 getSalary method in Employee class;
// give Lawyers a $5K raise
public double getSalary() {
return 45000.00;
}
}

45
super keyword
• Subclasses can call overridden methods with super
super.method(parameters)

– Example:
public class Lawyer extends Employee {
// give Lawyers a $5K raise (better)
public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary + 5000.00;
}
}

– This version makes sure that Lawyers always make $5K 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 StutterIntList extends ArrayIntList {
private int stretch;
public StutterIntList(int stretchFactor) {
super();
stretch = stretchFactor;
}
public StutterIntList(int stretchFactor, int capacity) {
super(capacity);
stretch = stretchFactor;
}
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 getStretch() {
return stretch;
}
}
48
Subclasses and fields
public class Employee {
private double salary;
...
}

public class Lawyer extends Employee {


...
public void giveRaise(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:
– ArrayIntList index 0 1 2
value 42 -3 17

data next data next data next


– LinkedIntList front 42 -3 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, SortedMap

• An ADT can be implemented in multiple ways by classes:


– ArrayList and LinkedList implement List
– HashSet and TreeSet implement Set
– LinkedList , ArrayDeque, etc. implement Queue

• Exercise: Create an ADT interface for the two list classes.


52
An IntList interface (16.4)

// Represents a list of integers.


public interface IntList {
public void add(int value);
public void add(int index, int value);
public int get(int index);
public int indexOf(int value);
public boolean isEmpty();
public void remove(int index);
public void set(int index, int value);
public int size();
}

public class ArrayIntList implements IntList { ...


public class LinkedIntList implements IntList { ...
53
Our list classes
• We have implemented the following two list collection classes:
– ArrayIntList index 0 1 2
value 42 -3 17

data next data next data next


– LinkedIntList front 42 -3 17

– Problem:
• They can store only int elements, not any type of value.

54
Type Parameters (Generics)
ArrayList<Type> name = new ArrayList<Type>();

• Recall: When constructing a java.util.ArrayList, you


specify the type of elements it will contain between < and >.
– We say that the ArrayList class accepts a type parameter,
or that it is a generic class.

ArrayList<String> names = new ArrayList<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 (15.4)

public class Foo<T> {


private T myField; // ok

public void method1(T param) {


myField = new T(); // error
T[] a = new T[10]; // error
}
}

– You cannot create objects or arrays of a parameterized type.

57
Generics/arrays, fixed
public class Foo<T> {
private T myField; // ok

public void method1(T param) {


myField = param; // ok
T[] a2 = (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 ArrayList<E> {
...
public int indexOf(E value) {
for (int i = 0; i < size; i++) {
// if (elementData[i] == value) {
if (elementData[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 ListNode<E> {
public E data;
public ListNode<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 indexOf(E value);
public boolean isEmpty();
public void remove(int index);
public void set(int index, E value);
public int size();
}

public class ArrayIntList<E> implements IntList<E> { ...


public class LinkedIntList<E> implements IntList<E> { ...

61

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy