18. Bag Implementations
Given the definition of a
BagAnd given the definitions of the specialized bags
IndexedBagSortedBag
How can they be implemented?
18.1. Array Implementation Idea
A representation of how a Bag could be implemented with an array.
The idea will be similar to the
StackandQueuearray based implementationsUnlike a
Queue, the front will always be at index 0With bags, adding and removing can happen anywhere
Adding to the middle would require a linear time operation to make room
Removing from the middle would require a linear time operation to eliminate the gap
An
expandCapacitywill be requiredThe simpler version though since the front is always index 0
The functionality of a
Bag, regardless of what specific version it isboolean add(T element)boolean remove(T element)boolean contains(T element)int count(T element)boolean isEmpty()int size()Iterator<T> iterator()
18.2. ArrayIndexedBag
In addition to the functionality of the
Bag, anIndexedBagmust also be able toboolean add(int index, T element)T remove(int index)T set(int index, T element)T get(int index)int indexOf(T element)
Note
For brevity, only a subset of methods are included below. See the
ArrayIndexedBag class for the full implementation.
7/**
8 * Implementation of an IndexedBag with an array as the container. The array container will automatically "grow" to
9 * accommodate adding beyond the initial capacity.
10 *
11 * @param <T> Type of elements that are to be in the IndexedBag
12 */
13public class ArrayIndexedBag<T> implements IndexedBag<T> {
14
15 private static final int DEFAULT_CAPACITY = 100;
16 private static final int NOT_FOUND = -1;
17 private T[] bag;
18 private int rear;
19
20 /**
21 * Create an empty ArrayIndexedBag of the default capacity.
22 */
23 public ArrayIndexedBag() {
24 this(DEFAULT_CAPACITY);
25 }
26
27 /**
28 * Create an empty ArrayIndexedBag with the specified capacity.
29 *
30 * @param initialCapacity Starting capacity of the fixed length array
31 */
32 @SuppressWarnings("unchecked")
33 public ArrayIndexedBag(int initialCapacity) {
34 bag = (T[]) new Object[initialCapacity];
35 rear = 0;
36 }
Note the import of
Iteratorand the implementation ofIterator<T>Iterators are used for iterating over a collection
More on this later
18.2.1. Private Methods
41 /**
42 * Shifts elements in an array down (towards index 0) to the starting index specified. The element at the starting
43 * index will be overwritten.
44 *
45 * @param start Index of element to be overwritten and where shifting moves down to
46 */
47 private void shiftLeft(int start) {
48 for (int i = start; i < rear - 1; i++) {
49 bag[i] = bag[i + 1];
50 }
51 bag[rear - 1] = null;
52 }
53
54 /**
55 * Shifts elements in an array up (towards index rear) away from the starting index specified. The array location
56 * at the specified starting index will be open. This method assumes there is room in the array to facilitate the
57 * shifting.
58 *
59 * @param start Index of where the array has a new open location and where shifting moves up from
60 */
61 private void shiftRight(int start) {
62 for (int i = rear; i > start; i--) {
63 bag[i] = bag[i - 1];
64 }
65 bag[start] = null;
66 }
shiftLeftandshiftRightThese move elements up or down the array to make or eliminate room for adding and removing elements
18.2.2. Iterator Method
Warning
Iterators are the focus of another topic, so they are only briefly presented here.
Iterators are used to provide a common way to iterator over a collection, regardless of the underlying container
Array vs. linked structure
186 @Override
187 public Iterator<T> iterator() {
188 return new ArrayIterator<>(bag, size());
189 }
All this method does is create an instance of an
ArrayIteratorand return itWhat the
ArrayIteratorclass looks like is discussed later in the course
18.2.3. Add Methods
89 @Override
90 public boolean add(T element) {
91 return add(rear, element);
92 }
93
94 @Override
95 public boolean add(int index, T element) {
96 // Index == size() is valid as that just appends
97 if (index < 0 || index > size()) {
98 throw new IndexOutOfBoundsException(index);
99 }
100 if (size() == bag.length) {
101 bag = Arrays.copyOf(bag, bag.length * 2);
102 }
103 shiftRight(index);
104 bag[index] = element;
105 rear++;
106 return true;
107 }
Note that
add(T element)simply delegates toadd(int index, T element)for ease and code/logic reuseUnlike the methods for adding to a
StackorQueue, this method may throw an exceptionThe exception is thrown if the specified index is out of bounds
Like the
StackandQueue, the array may run out of spaceUnlike before, an
expandCapacitymethod is not includedInstead, the
copyOffunction from theArraysclass is usedIt creates a new array with the specified capacity containing a copy of the elements in the original array
The
shiftRightprivate method is used to make room for the element to be added
18.2.4. Remove
129 @Override
130 public T remove(int index) {
131 if (index < 0 || index >= size()) {
132 throw new IndexOutOfBoundsException(index);
133 }
134 T returnElement = bag[index];
135 shiftLeft(index);
136 rear--;
137 return returnElement;
138 }
139
140 @Override
141 public boolean remove(T element) {
142 if (!contains(element)) {
143 return false;
144 }
145 int removeIndex = indexOf(element);
146 remove(removeIndex);
147 return true;
148 }
The
remove(T element)method delegates to theremove(int index)for ease and code/logic reuse
18.3. ArraySortedBag
In addition to the functionality of the
Bag, aSortedBagmust also be able toT removeFirst()T removeLast()T first()T last()
Further, the overloaded
addandremovemethods must preserve the ordering of the elements
Note
For brevity, only a subset of methods are included below. See the
ArraySortedBag class for the full implementation.
7/**
8 * Implementation of a SortedBag with an array as the container. The array container will automatically "grow" to
9 * accommodate adding beyond the initial capacity.
10 *
11 * @param <T> Type of elements that are to be in the SortedBag
12 */
13public class ArraySortedBag<T extends Comparable<? super T>> implements SortedBag<T> {
14
15 private static final int DEFAULT_CAPACITY = 100;
16 private static final int NOT_FOUND = -1;
17 private T[] bag;
18 private int rear;
19
20 /**
21 * Create an empty ArraySortedBag of the default capacity.
22 */
23 public ArraySortedBag() {
24 this(DEFAULT_CAPACITY);
25 }
26
27 /**
28 * Create an empty ArraySortedBag with the specified capacity.
29 *
30 * @param initialCapacity Starting capacity of the fixed length array
31 */
32 @SuppressWarnings("unchecked")
33 public ArraySortedBag(int initialCapacity) {
34 bag = (T[]) new Comparable[initialCapacity];
35 rear = 0;
36 }
Notice
<T extends Comparable<? super T>>There is a little bit to unpack here
First, when something extends Comparable, it means that the type has some defined ordering
The method
compareTois implemented for the type
If
x.compareTo(y)is calledReturn a negative integer if
x < yReturn zero if
x == yReturn a positive integer if
x > y
When something extends
Comparable<T>, that meansthiscan be compared to some typeTthiscan be compared to things of typeT, but not the other way around
T extends Comparable<T>means that the typeTcan be compared to things of typeTto provide some defined orderingWhich is needed, if the elements are to be sorted
Finally,
<T extends Comparable<? super T>>means thatTcan be compared to something of typeTor a superclass ofT?is a wildcard
Thus, this means that
Tmust have a defined ordering for itself through eitherA direct implementation of
compareToInheritance
18.3.1. Adding Method
88 @Override
89 public boolean add(T element) {
90 if (size() == bag.length) {
91 bag = Arrays.copyOf(bag, bag.length * 2);
92 }
93 int insertIndex = findInsertIndex(element);
94 shiftRight(insertIndex);
95 bag[insertIndex] = element;
96 rear++;
97 return true;
98 }
99
100 /**
101 * Linear search through the bag to find the index of where the element should be inserted. If equal elements
102 * exist within the collection, the returned index will be after the existing equal elements.
103 *
104 * @param element Item to be inserted
105 * @return Index where the element should be inserted
106 */
107 private int findInsertIndex(T element) {
108 int searchIndex = 0;
109 for (T bagElement : this) {
110 if (element.compareTo(bagElement) <= 0) {
111 return searchIndex;
112 }
113 searchIndex++;
114 }
115 // Element must belong at rear
116 return rear;
117 }
The
addmethod makes use of a private methodfindInsertIndexfindInsertIndexmakes use of the class’ iterator method to iterate over the collectionIt is simply used to perform a linear search
It also makes use of the
compareTomethodRemember, the elements themselves determine the ordering
One does not know what the type
Tis, so how can they be compared?If they’re numbers,
<,>,==, would workBut what if sorting strings? Colours?
Since
Tmust have acompareToimplemented, it can be used to guarantee a proper ordering, regardless of the typeTmust have acompareTosince it must extendComparable<? super T>
Given this, the loop executes until it finds the index where the element to be inserted is less than the current element in the collection
Or, in other words, it loops while
There are more elements in the collection
The thing to be inserted belongs after the current element in the collection
18.4. Linked Implementation
A representation of how a Bag could be implemented with a linked structure.
Although not discussed here, a linked implementation of the bags could also be written
Reviewing the different types of insertions and removals from a linked structure would help
18.5. For Next Time
Note
Note that there are better implementations of these data structures. One will be discussed later in the course.
Read Chapter 6 Sections 6 & 7
17 pages
18.5.1. Playing Code
Download and play with