CarrCh05v2

CarrCh05v2 - C H A P T E R 5 List Implementations That Use...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: C H A P T E R 5 List Implementations That Use Arrays CONTENTS Using a Fixed-Size Array to Implement the ADT List An Analogy The Java Implementation Using Dynamic Array Expansion to Implement the ADT List Expanding an Array A New Implementation of a List Using a Vector to Implement the ADT List A Summary of Methods in the Class Vector The Pros and Cons of Using an Array to Implement the ADT List Java Class Library The Class ArrayList The Interface Serializable PREREQUISITES Chapter Chapter 3 4 Designing Classes Lists OBJECTIVES After studying this chapter, you should be able to G G Implement the ADT list by using a fixed-size array, an array that you expand dynamically, or an instance of Vector Discuss the advantages and disadvantages of the three implementations presented You have seen several examples of how to use the ADT list in a program. This chapter presents three different ways that you can implement a list in Java. Each of these ways involves an array. You will see a completely different implementation in the next chapter. 97 98 CHAPTER 5 List Implementations That Use Arrays We begin by using an ordinary Java array to represent the entries in a list. With this implementation, your list could become full, just as a handwritten list can fill a page. We then offer two other implementations that do not suffer from this problem. When you use all of the space in an array, Java enables you to move the data to a larger array. The effect is to have an array that apparently expands to meet your needs. Alternately, you can use an instance of the Java class Vector to represent the list entries. The result is like using an array that can expand, since the underlying implementation of Vector uses such an array. But this list implementation is simpler to write than one that uses an array, because Vector does the work for you. Using a Fixed-Size Array to Implement the ADT List We begin by using an analogy to describe how a fixed-size array could represent a list. In doing so, we show how the add and remove methods would work. Subsequently, we present a corresponding Java implementation for the list. An Analogy 5.1 Figure 5-1 Imagine a classroom—call it room A—containing 40 desks in a fixed position. If a course is restricted to 30 students, 10 desks are idle and wasted. If we lift the enrollment restriction, we can accommodate only 10 more students, even if 20 more want to take the course. An array is like this classroom, and each desk is like one array location. Suppose that we number the 40 desks in the room sequentially, beginning with zero, as Figure 5-1 illustrates. Although desks are arranged in rows in typical classrooms, we will ignore this detail and treat the desks as a one-dimensional array. A classroom that contains desks in a fixed position 39 38 31 37 30 23 24 17 10 3 32 25 18 11 4 33 26 19 12 5 34 27 20 13 6 35 28 21 14 7 36 29 22 15 16 9 2 8 1 0 om Ro A Using a Fixed-Size Array to Implement the ADT List 99 Suppose that the first student who arrives at the classroom sits at desk 0; the second student sits at desk 1, and so on. Eventually, 30 students occupy the desks numbered 0 through 29. They are organized by arrival time. The instructor knows immediately who arrived first (that person is at desk 0) and who arrived last (that person is at desk 29). Additionally, the instructor could ask for the name of the student seated at any particular desk, just as a programmer can access any array element directly. Thus, the instructor could ask for each student’s name in order of arrival—by polling desks 0 through 29—or in reverse order—by polling desks 29 through 0. This action is called a traversal, or iteration, of the data. When you use an array to organize data in this manner, the implementation is said to be array based. Instead of arranging the students in room A by arrival time, suppose that we arrange them alphabetically by name. Doing so requires a sorting algorithm, such as the ones that Chapters 11 and 12 will discuss. That is, the ADT list does not choose the order of its entries; the client must do so. 5.2 Figure 5-2 Adding a new student. Imagine that we have already arranged the students in room A alphabetically by name. Suppose that a new student wants to join the students already in the room. Recall that the 30 occupied desks are numbered sequentially from 0 to 29. Since 40 desks are in the room, the desk numbered 30 is available. When the students were arranged by arrival time, we would simply have assigned the new student to desk 30. Since the students are now arranged alphabetically by name, we must do more work. Suppose that the new student belongs between the two students that occupy desks 10 and 11. That is, the new student’s name is alphabetically between the names of the two students that occupy desks 10 and 11. Since the desks are in fixed positions, the new student must occupy desk 11. Before the new student can be seated, the student currently at desk 11 needs to move to desk 12, as Figure 5-2 illustrates. This requirement, however, causes a chain reaction: The student currently at desk 12 needs to move to desk 13, and so on. That is, each student seated in desks 11 through 29 must move to the next higher-numbered desk. If only one student moves at a time, the student in desk 29 must move to desk 30 before the student in desk 28 can move to desk 29, and so on. As you can see, adding a new student requires moving several other students. However, we do not disturb the students seated in the desks that are before the new student’s desk— desks 0 through 10 in our example. Seating a new student between two existing students: at least one other student must move 12 11 Ed 10 Carol Ed, please move back one desk. Doug Question 1 In the previous example, under what circumstance could you add a new student alphabetically by name without moving any other student? 100 CHAPTER 5 List Implementations That Use Arrays 5.3 Removing a student. Now imagine that the student in desk 5 of room A drops the course. The desk stays in its fixed location within the room. If we still want students to sit in consecutively numbered desks, several students will need to move. In fact, each student in desks 6 through 30 must move to the next lower-numbered desk, beginning with the student in desk 6. That is, if only one student moves at a time, the student in desk 6 must move to desk 5 before the student in desk 7 moves to desk 6, and so on. Question 2 What is an advantage of moving students as just described so that the vacated desk does not remain vacant? Question 3 What is an advantage of leaving the vacated desk vacant? The Java Implementation 5.4 The Java array-based implementation for the ADT list incorporates some of the ideas that our classroom example illustrates. The implementation is a class AList1 that implements the interface ListInterface that you saw in Chapter 4. The private data fields are G G G An array of objects An integer that counts the number of entries in the list An integer constant that defines the size of the array The Java definitions for these items appear as follows: private Object entry; // array of list entries private int length; // current number of entries in list private static final int MAX_SIZE = 50; // max length of list Each ADT operation corresponds to a public method within the class. 5.5 The class AList has the following form. Notice the overall organization of the class, the private data, the default constructor, and the simple implementations of the methods clear, getLength, isEmpty, isFull, and display. We will provide implementations for the other methods shortly. public class AList implements ListInterface { private Object entry; // array of list entries private int length; // current number of entries in list private static final int MAX_SIZE = 50; // max length of list public AList() { length = 0; entry = new Object[MAX_SIZE]; } // end default constructor public AList(int maxSize) { length = 0; 1. Ordinarily we would name this class ArrayList. But as you will see at the end of this chapter, Java already provides a class with that name. Although we certainly could have named our class ArrayList as well, we chose a different name to avoid confusion. Using a Fixed-Size Array to Implement the ADT List entry = new Object[maxSize]; } // end constructor public boolean add(Object newEntry) { < Implementation deferred > } // end add public boolean add(int newPosition, Object newEntry) { < Implementation deferred > } // end add public Object remove(int givenPosition) { < Implementation deferred > } // end remove public void clear() { length = 0; < But see Question 4. > } // end clear public boolean replace(int givenPosition, Object newEntry) { < Implementation deferred > } // end replace public Object getEntry(int givenPosition) { < Implementation deferred > } // end getEntry public boolean contains(Object anEntry) { < Implementation deferred > } // end contains public int getLength() { return length; } // end getLength public boolean isEmpty() { return length == 0; } // end isEmpty public boolean isFull() { return length == entry.length; } // end isFull public void display() { for (int index = 0; index < length; index++) System.out.println(entry[index]); } // end display } < This class will define two private methods that will be discussed later. > // end AList 101 102 CHAPTER 5 List Implementations That Use Arrays Question 4 The method clear sets length to zero. Although the list methods will correctly behave as though the list is empty, the objects that were in the list will remain allocated. Suggest at least two ways to deallocate these objects. 5.6 The first add method. Now consider the implementations that we deferred. Adding a new entry to the end of the list is easy; we simply add the entry to the array immediately after its last occupied location. Of course, since we are using a fixed-size array, adding a new entry is possible only if the array has available space. If the array is full, our implementation returns false. Thus, the first add method has the following implementation: public boolean add(Object newEntry) { boolean isSuccessful = true; if (!isFull()) { // position of new entry will be after last entry in list, // that is, at position length+1; corresponding array index is // 1 less than position, so index is length entry[length] = newEntry; length++; } else isSuccessful = false; return isSuccessful; } // end add 5.7 The second add method. Adding a new entry at an arbitrary position within the list is like adding a student to room A in our example in Segment 5.2. Although that example positions students alphabetically by their names, remember that the list’s client—not the list itself—determines the desired position of each entry. Thus, if that position is before the end of the list, we need to shift existing entries to vacate the desired location so that it can accommodate the new entry. If the addition is to the end of the list, no such shift is necessary. In either case, space must be available in the array to accommodate a new entry. The following implementation of add uses a private method makeRoom to handle the details of moving data within the array. Remember that we can add to the list at positions that range from 1 to the length of the list plus 1. public boolean add(int newPosition, Object newEntry) { boolean isSuccessful = true; if (!isFull() && (newPosition >= 1) && (newPosition <= length+1)) { makeRoom(newPosition); entry[newPosition-1] = newEntry; length++; } else isSuccessful = false; return isSuccessful; } // end add Using a Fixed-Size Array to Implement the ADT List 103 Now we must implement the private method makeRoom. Typically, the method shifts list entries toward the end of the array, beginning with the last entry, as Figure 5-3 illustrates. (For simplicity, our figures and discussion portray the objects as if they were actually in the array. In reality, the array contains references to the objects.) However, if newPosition is length + 1, the addition is at the end of the array, so no shift is necessary. In this case, makeRoom does nothing, since its for statement exits immediately. /** Task: Makes room for a new entry at newPosition. * Precondition: 1 <= newPosition <= length+1; * length is list’s length before addition. */ private void makeRoom(int newPosition) { // move each entry to next higher position, starting at end of // list and continuing until the entry at newPosition is moved for (int index = length; index >= newPosition; index--) entry[index] = entry[index-1]; } // end makeRoom Notice that the add method enforces the preconditions of makeRoom. Figure 5-3 Making room to insert Carla as the third entry in an array Alice Bob Doug Haley Alice Bob Doug Haley Haley Alice Bob Doug Doug Haley Alice Bob Carla Doug Haley Question 5 You could implement the first add method, which adds an entry to the end of the list, by invoking the second add method, as follows: public boolean add(Object newEntry) { return add(length+1, newEntry); } // end add Discuss the pros and cons of this revised approach. Question 6 a. b. c. Suppose that myList is a list that contains the five entries a b c d e. What does myList contain after executing myList.add(5, w)? Starting with the original five entries, what does myList contain after executing myList.add(6, w)? Which of the operations in Parts a and b of this question require elements in the array to shift? 104 CHAPTER 5 List Implementations That Use Arrays Question 7 If myList is a list of five entries, each of the following statements adds a new entry to the end of the list: myList.add(newEntry); myList.add(6, newEntry); Which way requires fewer operations? 5.8 The remove method. Removing a list entry at an arbitrary position is like a student leaving room A in our example in Segment 5.3. We need to shift existing entries to avoid a gap in the array, except when removing the list’s last entry. The following implementation uses a private method removeGap to handle the details of moving data within the array: public Object remove(int givenPosition) { Object result = null; // return value if ((givenPosition >= 1) && (givenPosition <= length)) { result = entry[givenPosition-1]; // get entry to be removed // move subsequent entries toward entry to be removed, // unless it is last in list if (givenPosition < length) removeGap(givenPosition); length--; } // end if return result; } // end remove // return reference to removed entry, // or null if givenPosition is invalid Question 8 When a list is empty, the data field method return in this case? Why? length contains zero. What does the remove The following private method removeGap shifts list entries toward the entry that is removed, as Figure 5-4 illustrates. Beginning with the entry after the one to be removed and continuing until the end of array, removeGap moves each entry to its next lower position. /** Task: Shifts entries that are beyond the entry to be removed * to next lower position. * Precondition: 1 <= givenPosition <= length; * length is list’s length before removal. */ private void removeGap(int givenPosition) { for (int index = givenPosition; index < length; index++) entry[index-1] = entry[index]; } // end removeGap Note that no shift is necessary if the deletion is at the end of the array. In that case, the last entry in the list is at position length, since the first entry is at position 1. Thus, givenPosition would equal length, and so the for statement in removeGap would exit immediately. Also notice that the remove method enforces the preconditions of removeGap. In particular, removeGap is not invoked when the list is empty. Using a Fixed-Size Array to Implement the ADT List Figure 5-4 105 Removing Bob by shifting array entries Alice Carla Doug Haley Alice Carla Carla Doug Haley Alice Carla Doug Doug Haley Alice Carla Doug Haley Haley Alice 5.9 Bob Carla Doug Haley The methods replace and getEntry. Replacing a list entry and retrieving a list entry are two straightforward operations when an array is used to represent the entries. You simply replace or retrieve the object that is in the indicated array location. The following methods implement these two operations: public boolean replace(int givenPosition, Object newEntry) { boolean isSuccessful = true; if ((givenPosition >= 1) && (givenPosition <= length)) entry[givenPosition-1] = newEntry; else isSuccessful = false; return isSuccessful; } // end replace public Object getEntry(int givenPosition) { Object result = null; // result to return if ((givenPosition >= 1) && (givenPosition <= length)) result = entry[givenPosition-1]; return result; } // end getEntry Question 9 5.10 What is an advantage of using an array to organize data? What is a disadvantage? The method contains. The method getEntry locates the entry at a given position by going directly to the appropriate array element. In contrast, the method contains must search the array for a given entry. Beginning at index zero, the method examines each array element until it either locates the desired entry or reaches the end of the array without success. In the following implementation, we use a local boolean variable to terminate the loop when we find the desired entry: 106 CHAPTER 5 List Implementations That Use Arrays public boolean contains(Object anEntry) { boolean found = false; for (int index = 0; !found && (index < length); index++) { if (anEntry.equals(entry[index])) found = true; } // end for return found; } // end contains This way of looking for a particular entry in an array is called a sequential search. Chapter 16 discusses this technique further and presents another algorithm that is generally faster. Note: Using a fixed-size array to implement the ADT list limits the size of the list. Some lists naturally have a finite size, so this implementation is both appropriate and useful. For other lists, one of the other implementations given in this chapter and the next one would be more fitting. Using Dynamic Array Expansion to Implement the ADT List 5.11 An array, of course, has a fixed size. You decide on a maximum size for your array when you write your program. Segment 5.4 of the previous section used an array of MAX_SIZE memory locations to represent the entries in the list. When the array, and hence the list, becomes full, the method isFull returns true and the add methods return false. The fixed-size array in the previous implementation of the ADT list is like our classroom. If the room contains 40 desks but only 30 students, we waste 10 desks. If 40 students register for the course, the room is full and cannot accommodate anyone else. Likewise, if we do not use all locations in an array, we waste memory. If we need more, we are out of luck. For example, the implementation in the previous section denies a request to add to a list that already contains MAX_SIZE entries. Some applications can use a list that has a limited length. For example, a list of airline passengers and a list of ticket holders to a movie should not exceed a known maximum. For other applications, however, the length of a list can grow without bound. We will now show you how a list can be as long as you want but still use an array to represent its entries. Expanding an Array 5.12 One way to accommodate additional students is to use a larger room. Suppose that you are seated at your desk waiting for class to start. The professor arrives and announces that the class must move to a larger room. Suppose that the students leave one room and move to another without changing desk numbers. That is, a student at desk n in the old room will occupy desk n in the new room. In a similar manner, when an array becomes full, you can move its contents to a larger array. This process is called the dynamic expansion of an array. Figure 5-5 shows two arrays: an original array of five consecutive memory locations and another array—twice the size of the original array—that is in another part of the computer’s memory. If you copy the data from the original smaller array to the first five locations in the new larger array, the result will be like expanding the original array. The only glitch in this scheme is the name of the new array: You want it to be the same as the name of the old array. This is possible, as you will see momentarily. Using Dynamic Array Expansion to Implement the ADT List Figure 5-5 107 The dynamic expansion of an array copies the array’s contents to a larger second array Original array Larger array 5.13 Let’s work with a simple array of integers: int myArray = new int[MAX_SIZE]; At this point, myArray references the array in Figure 5-6a. Next, we’ll save the reference to this array by writing int oldArray = myArray; // save reference to myArray Both oldArray and myArray contain the same value, namely the address of the array. That is, old- Array and myArray each reference the array, as Figure 5-6b illustrates. For example, oldArray[0] and myArray[0] reference the same location: the first location of the array. Now we’ll allocate a new, larger array, but we’ll let only myArray reference it: myArray = new int[2*oldArray.length]; // double size of array Figure 5-6c illustrates the two arrays. Figure 5-6 (a) An array; (b) the same array with two references; (c) the two arrays, with the reference to the original array now referencing a new, larger array (a) myArray (b) myArray oldArray (c) myArray oldArray Finally, we’ll copy the data from the original array (oldArray) to the new array (myArray): for (int index = 0; index < oldArray.length; index++) myArray[index] = oldArray[index]; 5.14 Expanding the size of an array is not as attractive as it might first seem. Each time you expand an array, you must copy its contents. If you were to expand an array by one element each time you needed additional space in the array, the process would be expensive. For example, if a 50-element 108 CHAPTER 5 List Implementations That Use Arrays array represented a list of 50 entries, adding an entry to the list would require that you copy the array to a 51-element array. Adding another entry would require that you copy the 51-element array to a 52-element array, and so on. Each add operation would cause the array to be copied. If you added 50 entries to the original 50-entry list, you would copy the array 50 times. However, expanding the array by m elements spreads the copying cost over m additions instead of just one. Doubling the size of an array each time it becomes full is a typical approach. For example, when you add an entry to a full list of 50 entries, you copy the 50-element array to a 100-element array before completing the addition. The next 49 additions then can be made quickly without copying the array. Thus, you will have added 50 entries to the original list but will have copied the array only once. Note: During dynamic array expansion, the elements of an array are copied to a larger array. You should expand the array sufficiently to reduce the impact of the cost of copying. A New Implementation of a List 5.15 You can use these ideas to revise the implementation of the ADT list that the previous section presented. Only isFull and the two add methods are affected in the revised implementation. To begin, change the method isFull to always return false. Since we will expand the array when it becomes full, the list is never full. Instead of ignoring an addition when the array is full, each of the revised add methods doubles the array by calling a new private method doubleArray. To detect when the array is full, we define another private method isArrayFull, whose implementation is the same as the method isFull in the class AList. (See Segment 5.5.) For example, the first add method would appear as follows: public boolean add(Object newEntry) { if (isArrayFull()) doubleArray(); // add new entry after last current entry entry[length] = newEntry; length++; return true; } // end add You would make similar changes to the second add method. Question 10 Revise the second add method that adds an entry at a given position within the list by using dynamic array expansion. 5.16 The private method appears as follows: doubleArray incorporates our recent discussion of expanding an array, and /** Task: Doubles the size of the array of list entries. */ private void doubleArray() { Object oldList = entry; // save reference to array of // list entries int oldSize = oldList.length; // save old max size of array entry = new Object[2*oldSize]; // double size of array Using a Vector to Implement the ADT List 109 // copy entries from old array to new, bigger array for (int index = 0; index < oldSize; index++) entry[index] = oldList[index]; } // end doubleArray This completes the revised implementation. We will call the new class DynamicArrayList. 5.17 If the method isFull always returns false, why bother implementing it? The short answer is that isFull is in ListInterface, so any class that implements ListInterface must define it. If DynamicArrayList did not implement isFull, we would have to declare the class as abstract. Thus, we would not be able to create an instance of DynamicArrayList. Should we omit isFull from ListInterface? If we do, our array-based implementation AList can still define isFull. That is, a class that implements an interface can contain methods that are not specified in the interface. However, let’s see what happens if we do omit isFull from ListInterface. Suppose that AList implements isFull, but DynamicArrayList does not. If the client uses isFull, changing from one implementation of the ADT list (AList) to another (DynamicArrayList) would not be as simple as it should be. We would need to modify the client so that it did not use isFull. In addition, any instance of ListInterface, such as myList in the statement ListInterface myList = new AList(); would be unable to invoke isFull, even though AList implements it. Programming Tip: A class implementing a single interface that declares the operations of an ADT should define the methods declared in the interface as its only public methods. The class can also define private methods and protected methods. Using a Vector to Implement the ADT List 5.18 Figure 5-7 One way to let a list grow as needed is to use dynamic array expansion, as the previous section describes. Another way uses an instance of Java’s Vector class to represent the list’s entries. The class Vector provides the capabilities of an array that expands dynamically, but it hides the details of the process. Vector, which is in the package java.util, has methods that manipulate its entries in ways that are useful to an implementation of the ADT list. If we store our list entries in an instance of Vector, we can use Vector’s methods to manipulate our list entries. Figure 5-7 shows a client interacting with a list by using the methods in ListInterface. The implementations of these methods in turn interact with Vector’s methods to produce the desired effects on the list. A client uses the methods given in ListInterface, but the implementation of the list uses methods to perform its operations Vector Client ListInterface Instance of Vector Implementation of a list 110 CHAPTER 5 List Implementations That Use Arrays 5.19 We begin the class that implements ListInterface as follows: import java.util.Vector; public class VectorList implements ListInterface { private Vector entry; // entries in list . . . We must provide the import statement to indicate that the code that follows uses the class Vector from the package java.util. Often programmers will replace Vector in this statement with an asterisk to make all classes in the package java.util available to their programs. The data field entry is now an instance of Vector instead of an array, as it was earlier in this chapter. Since a vector keeps track of the number of entries it contains, a data field length is not required. Any time that we want the number of entries in the vector, and hence the list, we can write entry.size(). The constructors for our class create an instance of Vector by invoking Vector’s constructors. Our default constructor simply invokes Vector’s default constructor: public VectorList() { entry = new Vector(); } // end constructor Here, Vector’s default constructor creates a vector that can hold 10 entries. This vector will double in size after it becomes full. Our second constructor enables the client to specify the initial capacity of the list. It invokes a corresponding constructor of Vector: public VectorList(int initialSize) { entry = new Vector(initialSize); } // end default constructor Here Vector’s constructor creates a vector that can hold initialSize entries. This vector also will double in size after it becomes full. 5.20 To add to the end of a list, you use Vector’s addElement method. This method adds a given object to the end of a vector. If necessary, the vector increases its capacity to accommodate the new entry. Thus, our add method does not test whether the vector is full: public boolean add(Object newEntry) { entry.addElement(newEntry); return true; } // end add To add an entry at a given position in the list, we use Vector’s insertElementAt method. This method acts much the same as the add method for the ADT list. However, Vector numbers its entries from 0 instead of from 1: public boolean add(int newPosition, Object newEntry) { boolean isSuccessful = true; if ((newPosition >= 1) && (newPosition <= entry.size()+1)) entry.insertElementAt(newEntry, newPosition-1); Using a Vector to Implement the ADT List 111 else isSuccessful = false; return isSuccessful; } // end add 5.21 Similarly, remove uses Vector’s method removeElementAt. Since removeElementAt is a void method and remove is not, remove must retrieve the entry to be deleted before it is actually removed from the list. We use the method elementAt to retrieve an entry from a vector at a given index. Thus, the following statements are the heart of remove’s implementation: result = entry.elementAt(givenPosition-1); entry.removeElementAt(givenPosition-1); The remove method then has the following implementation: public Object remove(int givenPosition) { Object result = null; // return value if ((givenPosition >= 1) && (givenPosition <= entry.size())) { result = entry.elementAt(givenPosition-1); entry.removeElementAt(givenPosition-1); } // end if return result; } // end remove 5.22 The method clear uses the statement entry.removeAllElements(); to remove all entries from the vector and hence from the list. The method replace uses the statement entry.setElementAt(newEntry, givenPosition-1); to replace a designated entry in the list. The method getEntry uses the statement result = entry.elementAt(givenPosition-1); to retrieve a particular entry from the list. The implementations of the methods contains, getLength, isEmpty, and display are simple and left as exercises. As in the previous section, the method isFull always returns false. 5.23 Java’s class Vector and our class VectorList are similar in their functionality. VectorList simply invokes methods of the class Vector in its implementation of ListInterface. VectorList is an example of an adapter class, which we described in Segment 2.3 of Chapter 2. Writing VectorList is certainly easier than writing either of the other two array-based implementations that this chapter describes. However, since VectorList uses an instance of Vector instead of an array, its methods typically are less efficient than those of the other implementations. Note: Implementations of the ADT list that use either dynamic array expansion or an instance of Vector let the list grow as needed. 112 CHAPTER 5 List Implementations That Use Arrays A Summary of Methods in the Class Vector 5.24 We conclude this section with a description of the methods—including constructors—that we used from the class Vector. Descriptions of other methods in this class are available at http://java.sun.com/products/jdk/1.4/docs/api/index.html public Vector() Creates an empty vector with an initial capacity of 10. When the vector needs to increase its capacity, the capacity doubles. public Vector(int initialCapacity) Creates an empty vector with the specified initial capacity. When the vector needs to increase its capacity, the capacity doubles. public void addElement(Object newElement) Adds newElement to the end of the vector and increases its size by 1. The capacity of the vector is increased if that is required. public void insertElementAt(Object newElement, int index) Inserts newElement into the vector at the specified index. Each element in the vector with an index greater than or equal to index is shifted upward to have an index that is 1 greater than the value it had previously. The value of index must be greater than or equal to zero and less than or equal to the current size of the vector. The method throws ArrayIndexOutOfBoundsException if index is not in this range. The capacity of the vector is increased if that is required. Note that you can use this method to add an element after the last current element. public void removeElementAt(int index) Deletes the element at the specified index. Each element in the vector with an index greater than or equal to index is shifted downward to have an index that is 1 less than the value it had previously. The value of index must be greater than or equal to zero and less than the current size of the vector. The method throws ArrayIndexOutOfBoundsException if index is not in this range. public void removeAllElements() Removes all elements from the vector and sets its size to zero. public void setElementAt(Object newElement, int index) Replaces the element at the specified index to newElement. The element previously at that position is discarded. The value of index must be greater than or equal to zero and less than the current size of the vector. The method throws ArrayIndexOutOfBoundsException if index is not in this range. public Object elementAt(int index) Returns the element at the specified index. The value of index must be greater than or equal to zero and less than the current size of the vector. Throws ArrayIndexOutOfBoundsException if index is not in this range. public boolean contains(Object anEntry) Returns true if anEntry is an element of the vector; otherwise, returns false. public boolean isEmpty() Returns true if the vector is empty (that is, has a size of zero); otherwise, returns false. public int size() Returns the number of elements in the vector. The Pros and Cons of Using an Array to Implement the ADT List 113 The Pros and Cons of Using an Array to Implement the ADT List 5.25 This chapter discussed three implementations of the ADT list. Since Java’s class Vector uses an array in its implementation, all of this chapter’s implementations are based on an array. An array is simple to use and enables you to access any element immediately, if you know its index. Thus, the list’s retrieval operation getEntry is easy to write and quick to execute. Adding to the end of a list, and hence to the array, is equally easy and fast. On the other hand, using a fixed-size array limits the size of a list, which is usually a disadvantage. Using either dynamic array expansion or a vector enables you to increase the array’s size but requires copying data. Regardless of whether the array has a size that is fixed or dynamic, adding or removing entries that are between other entries requires shifting elements in the array. This data movement degrades the time efficiency of these operations, particularly when the list is long and the position of the addition or removal is near the beginning of the list. The implementation in the next chapter avoids this particular disadvantage but has disadvantages of its own. You should realize that the elements that we shift in the array are references, and so do not occupy much space nor take much time to move. Some languages other than Java store the data itself within the array. In that case, moving large, complex objects can be quite time-consuming. Note: When you use an array or vector to implement the ADT list, G G G G Retrieving an entry is fast Adding an entry at the end of a list is fast Adding or removing an entry that is between other entries requires shifting elements in the array Increasing the size of the array or vector requires copying elements Java Class Library The Java Class Library has a class that is similar the class AList that we defined in this chapter. It also has a special interface that enables you to write objects to a file with little effort. We introduce both of these components in this section. The Class ArrayList 5.26 Chapter 4 described the interface List that is in the standard Java package java.util. Recall that the methods in this interface are similar to the methods in our ListInterface. Other than some name changes, the major difference is in the numbering of the entries in a list. The first entry in our lists is at position 1, whereas the first entry in a list that adheres to the interface java.util.List is at index 0. The same package java.util that contains the interface List also contains a class ArrayList that implements List. This class uses dynamic array expansion, as we described earlier in this chapter. ArrayList is actually quite similar to the class Vector. Both classes are in the same package, and both implement the interface List. However, Vector contains all the methods of ArrayList as well as some others. You can learn more about these classes at http://java.sun.com/products/jdk/1.4/docs/api/index.html 114 CHAPTER 5 List Implementations That Use Arrays The Interface Serializable 5.27 You can represent an object as a sequence of bytes that you write to a file by using a process called object serialization. This process is possible for any instances of a class that implements the interface Serializable. This interface, which is in the package java.io, is empty, so you have no additional methods to implement. Adding only the words implements Serializable to the class’s definition is enough. For example, we could begin the class AList as follows: public class AList implements ListInterface, Serializable { . . . The Serializable interface tells the compiler that instances of AList can be serialized, that is, can be written to a file using object serialization. If the list myList is an instance of AList, any objects that are in myList must also belong to a class that implements Serializable. Such objects are serialized when myList is serialized. To serialize an object such as myList, you use the method writeObject from the class ObjectOutputStream. To reconstruct an object, you use the method readObject from the class ObjectInputStream. Appendix C provides an example of serialization and more information about writing and reading files. Programming Tip: A class that represents a collection of objects should implement the interface Serializable. By simply adding the word “Serializable” to the list of interfaces that a class implements, you can provide clients of that class with an easy way to place instances of the class in a file. C HAPTER S UMMARY The three implementations of the ADT list in this chapter use an array to store the items in a list. G Using an array results in relatively simple implementations of the list. G An array provides direct access to any of its elements, so a method such as getEntry has a simple, efficient implementation. G Using a fixed-size array can result in a full list. Using either dynamic array expansion or an instance of Vector avoids this drawback. G Adding an entry to or removing an entry from an array-based list typically requires that other entries shift by one position within the array. G Expanding the size of an array requires copying the contents of the array to a larger array. G Using an instance of Vector has the same advantages and disadvantages as using dynamic array expansion but results in an implementation that is much easier to write. G P ROGRAMMING T IPS G Object serialization enables you to write objects to a file. Such objects must belong to a class that implements the interface Serializable. G A class implementing a single interface that declares the operations of an ADT should define the methods declared in the interface as its only public methods. The class can also define private methods and protected methods. Java Class Library G E XERCISES 115 A class that represents a collection of objects should implement the interface Serializable. By simply adding the word “Serializable” to the list of interfaces that a class implements, you can provide clients of that class with an easy way to place instances of the class in a file. 1. Add a constructor to each of the classes AList, DynamicArrayList, and VectorList that creates a list from a given array of objects. 2. Suppose that you want an operation for the ADT list that removes the first occurrence of a given object from the list. The signature of the method could be as follows: public boolean remove(Object anObject) The method returns true if the list contained anObject and that object was removed. Write an implementation of this method for each of the three classes described in this chapter. 3. Suppose that you want an operation for the ADT list that returns the position of a given object in the list. The signature of the method could be as follows: public int getPosition(Object anObject) Write an implemention of this method for each of the three classes described in this chapter. 4. Exercise 5 in the previous chapter asked you to write statements at the client level that replace an object in a given list. Write a method at the client level that performs such a replacement. How does your method compare with the method replace of the ADT list? 5. Implement a method replace for the ADT list that returns the replaced object. Do this for each of the three classes described in this chapter. 6. Suppose that a list contains Comparable objects. Implement the following methods for each of the three classes described in this chapter. a. The method getMin that returns the smallest object in the list. b. The method removeMin that removes and returns the smallest object in the list. 7. Implement an equals method for the ADT list that returns true when the entries in one list equal the entries in a second list. In particular, add this method to the classes AList and VectorList. 8. Implement the methods contains, getLength, and isEmpty in the class VectorList. 9. Implement the method display in the class VectorList in two ways. One way uses the vector method elementAt. Another way uses the list method getEntry. P ROJECTS 1. Complete the implementation of the class DynamicArrayList. 2. Complete the implementation of the class VectorList. 3. Write a class that implements the interface ListInterface by using an instance of the class ArrayList. Compare your new class with the class VectorList. 116 CHAPTER 5 List Implementations That Use Arrays 4. Implement the interface ListInterface by using an array in which you ignore the first array location. Thus, you store the list’s ith entry in the array location at index i. 5. Implement as the class Bag the ADT bag that Project 1 of Chapter 4 described. Represent the bag as an array that you expand dynamically as necessary. Using Bag, implement a class PiggyBank as described in Project 2 of Chapter 4. 6. Repeat Project 5, but instead use an instance of Vector to represent the bag. 7. A set is a special bag that does not allow duplicates. Project 1 of Chapter 4 describes the ADT bag. Specify the operations for the ADT set. Implement the set by using a. An array that you expand dynamically as necessary b. An instance of Vector c. An instance of Bag ...
View Full Document

Ask a homework question - tutors are online