500 likes | 597 Views
Chapter 21. Implementing lists: array implementation. This chapter discusses. Implementing the class List . Implementations on a primitive language-provided structure called an array . Several important relationships between classes used to effect various implementations. Arrays.
E N D
Chapter 21 Implementing lists: array implementation
This chapter discusses • Implementing the class List. • Implementations on a primitive language-provided structure called an array. • Several important relationships between classes used to effect various implementations.
Arrays • array: a structure composed of a contiguous sequence of variables, all of the same type. • Individual variables are identified by index values or indexes. • Index values are integers, (beginning with 0 in Java). • Components or elements are the variables that comprise the array. • They are a primitive type (char, int, boolean, double, etc.) or a reference type (reference to an object).
Arrays (cont.) • The length of the array is the number of component variables that comprise it. • With length n, the component variables are indexed 0 through n-1.
Arrays (cont.) • The length of an array is fixed when the array is created. • Since contiguous memory is allocated for the variables, accessing a particular variable in the array requires a constant amount of time independent of the array length.
Arrays (cont.) • Suppose each variable in a particular array may take up four bytes, and memory is allocated starting at location 100 • The address of the variable with index i is 100+4*i. (starting address)+(variable size)*(element index) • The calculation of the address requires constant time, independent of the index and independent of the size of the array.
Array objects • Arrays in Java are encapsulated in objects. • Array objects have a public int component length. • The class of an array is determined by the type of its component variables. • The class is written as the component type followed by a pair of brackets. int[] Student[]
Defining arrays int[] grades; Student[] cs2125; int grades[]; Student cs2125[]; • The variable contains a reference to an array object. • We use a constructor to create the array object. grades = new int[5]; cs2125 = new Student[5];
Defining arrays (cont.) • The component variables of the arrays are initialized with standard default values. e.g. 0 for int, null for references. • The length given in the constructor need not (and generally should not) be a literal. cs2125 = new Student[studentCount]; grades = new int[4*cs2125.length];
Accessing array components • A reference to the array is followed by an index value in brackets. grades[3] = 100; grades[4] = grades[3]/2; cs2125[0] = new Student(…); cs2125[0].payFees(100); int i; for(i = 0; i < grades.length; i=i+1) grades[i] = 100; • The index value must be anint, non-negative, and less than the length of the array.
Accessing array components (cont.) • If an array is referenced with an index that is negative or is not less than the array length, an ArrayIndexOutOfBoundsException is thrown.
BoundedList public abstract class BoundedList A list of objects with a fixed maximum size. protected BoundedList (int maxSize) Create a new BoundedList with a specified maximum size. require: maxSize >= 0 ensure: isEmpty(new BoundedList(n)) • Since the class is abstract and an abstract constructor can be invoked only by a subclass, it is declared protected.
BoundedList (cont.) • Component variables: private Object[] elements; privateint size; • elements will hold the components of the array. • size will hold the current length of the list (not the maximum number of elements).
BoundedList (cont.) protected BoundedList (int maxSize){ Require.condition(maxSize >= 0); elements = new Object[maxSize]; size = 0; } public int size() { return this.size; } public Object get (int i) { Require.condition(0 <= i && i < size); return element[i]; } public void append (Object obj) { Require.notNull(obj); Require.condition(size < elements.length); elements[size] = obj; size = size+1; }
BoundedList (cont.) public void add (int i, Object obj) { Require.notNull(obj); Require.condition( 0 <= i && i <= size); Require.condition(size < elements.length); int j; for (j = size-1; j >= i; j = j-1) elements[j+1] = element[j]; elements[i] = obj; size = size + 1; } public void remove (int i) { Require.condition(0 <= i && i < size); int j; for ( j = i; j < size-1; j = j+1) elements[j] = element[j+1]; size = size - 1; }
BoundedList (cont.) public void set (int i, Object obj) { Require.notNull(obj); Require.condition( 0 <= i && i < size); elements[i] = obj; } public void clear () { size = 0; }
BoundedList (cont.) • The methods add (and remove) “shuffle” a portion of the array up or down. • Elements must be moved starting with the last, or else elements will be overwritten.
BoundedList (cont.) • For instance, if we write for ( j = i; j < size; j= j+1) elements[j+1] = elements[j];
BoundedList (cont.) • When elements are deleted with clear or remove, there is no need to set array components to null. • These elements are considered “invalid;” it does not matter what they contain.
BoundedList (cont.) public boolean isEmpty () { return this.size() == 0; } public int indexOf (Object obj) { int i; int n = this.size(); i = 0; while (i <n && !obj.equals(get(i))) i = i+1; if (i == n) return -1; else return i; }
BoundedList (cont.) public String toString () { String s = “[”; int n = this.size(); if (n > 0) { s = s + this.get(0).toString(); int i; for(i = 1; i < n; i = i+1) s = s + “, ” + this.get(i).toString(); } s= s + “]”; } public int maxSize () { return elements.length; }
The method copy • We make a shallow copy of the list. i.e. the original list and the copy reference the same list elements.
The method copy(cont.) public BoundedList copy() { int i; BoundedList theCopy = new BoundedList(elements.length); for (i = 0; i < size; i=i+1) { theCopy.elements[i] = this.elements[i]; theCopy.size = this.size; return theCopy; } //This doesn’t work because BoundedList is an abstract class.
Java Interface Cloneable • Object class has a method clone. protected Object clone () throws CloneNotSupportedException Create a copy of this Object. • This method has these properties s != s.clone() s.equals(s.clone()); s.clone() instanceof Student //of course we are implying that ‘s’ is a Student. • Since clone returns an object, it is often necessary to cast the result. Student s2 = (Student) s.clone();
Java Interface Cloneable (cont.) public interface Cloneable {} • Any class that supports the method clone should implement this interface. If not, a CloneNotSupportedException is thrown. • The method clone works essentially like this: if (this instanceof Cloneable) { Object copy = new instance of this class; for each component variable v of this copy.v = this.v; return copy; else throw CloneNotSupportedException;
Java Interface Cloneable (cont.) • Implementing the interface does not require a class to implement any methods; the class inherits the method clone implemented in the class Object.
Java Interface Cloneable (cont.) • If Java were built with multiple inheritance, Cloneable would be a class from which other objects could inherit and the method clone would be defined there.
Java Interface Cloneable (cont.) • It simply “turns on” the method clone inherited from Object.
BoundedList public abstract class BoundedList implements Clonable { … public Object clone() { BoundedList theCopy = (BoundedList)super.clone(); …
BoundedList (cont.) • We may want a less shallow copy of the list so that we can manipulate the list and its clone independently.
BoundedList (cont.) public Object clone() { try { BoundedList theCopy = (BoundedList)super.clone(); theCopy.elements = (Object[])this.elements.clone(); return theCopy; } catch (CloneNotSupportedException e) { return null; } }
Implementation • We need to cast the result explicitly to BoundedList or Object[ ] as appropriate. • Java’s scoping rules allow private components of the BoundedListtheCopy to be accessed directly in the method. • Since the Object method clone is specified as possibly throwing a CloneNotSupportedException, we must include a try statement and catch it, or include a throws clause in the specifications of the method.
Implementation (cont.) • In order to have an abstract class that creates a new object, we implement a method like this: public BoundedList copy () { return (BoundedList) this.clone(); } • The object created by the call to super.clone() will be of the same run-time class as the object executing the call.
Abstract Constructor • An abstract method can be used like a constructor for creating objects in a superclass. • Sometimes it is called a factory method. • Subclasses implement the method and determine which actual concrete class to instantiate. public abstract BoundedList createList (int maxSize) Create a new BoundedList with a specified maximum size. require: maxSize >= 0 ensure: isEmpty(createList(n))
Abstract constructor(cont.) public class BoundedStudentList extends BoundedList { … /** * Create a new BoundedStudent list * require: * maxSize >= 0 * ensure: * isEmpty(createList(n)) * createList(n) instanceof * BoundedStudentList */ public BoundedList createList (int maxsize) { return new BoundedStudentList (maxsize); } … }
Abstract constructor(cont.) public BoundedList copy() { int i; BoundedList theCopy = createList( this.element.size); for (i = 0; i < this.size; i=i+1) theCopy.elements[i]=this.elements[i]; theCopy.size = this.size; return theCopy; }
Advantages of arrays • Elements can be accessed efficiently; get, append, and set all operate in constant time.
Limitations of arrays • The array is static. The client must have a good idea of the ultimate size of a list when the list is created. • Too large means wasted space. • Too small causes failure. • Operations remove, add, and indexOf all are linear. • Conclusions • Not the best implementation for dynamic lists. • Often a good choice for static lists.
Dynamic arrays • A boundedlist has a maximum size. • A dynamic list has no maximum size. • A static array can be made into a dynamic array by simply creating a bigger array when the original array is full.
Dynamic lists public void append (Object obj) { Require.notNull(obj); if (this.size == elements.length) { // need a bigger array Object[] newArray = new Object[2*elements.length]; //copy contents of old array to new int i; for(i = 0; i < elements.length; i=i+1) newArray[i] = elements[i]; elements = newArray; } elements[size] = obj; size = size +1; }/*append becomes a very expensive operation because there is the possibility of having to copy the entire array into a new array. (linear time) */
Vector class • Vector class is a container class that encapsulates a “dynamic” use of arrays. public Vector (); public Vector (int initialCapacity); public Vector (int initialCapacity, int capacityIncrement); New Vector (100,20) default initialCapacity = 10 default capacityIncrement = 0 capacityIncrement of 0 means it doubles everytime it outgrows capacity.
Wrapper • A class that adds functionality to or modifies the specification of another class. • A class that provides required functionality but lacks the required interface can be tailored to fit our needs by wrapping the class in a wrapper class. • Vector has all of the functionality and most of the methods of lists.
Wrapper specifications public Object elementAt(int index); The element at the given array index. public int indexOf (Object elem) Index of the given element; -1 if the array does not contain the element. public int size () The number of elements in the Vector. public Object clone() A new copy of the Vector. public void addElement (Object elem) Add the element to the end of the Vector; size is increased, and capacity is incremented if necessary. public void insertElementAt (Object obj, int index) Add the given element at the given array index, shifting elements up as necessary.
Wrapper specifications (cont.) public void removeElementAt (int index) Remove the element at the given index, and shift higher indexed elements down. public void setElementAt (Object obj, int index) Replace the element at the specified array index with the Objectt provided. public String toString() A string representation of this Vector, containing the String representation of each element. public void removeAllElements() Removes all the components from this Vector and sets its size to zero. • The time complexity of the methods is the same as with BoundedList.
We’ve covered • Java’s array classes • Object arrays used to implement a version of the class List • Cloning arrays • Solving the fixed array limitation • Vectors • Dynamic arrays • Wrappers • Time complexities.