Implementing ArrayMap
Here is the Map interface:
public interface Map<K, V> {
boolean put(K key, V value); // stores value under key, replacing any old value;
// returns true if the key was not present before
V get(K key); // returns the value stored under key, or null if key is absent
boolean remove(K key); // removes key and its value if key is present;
// returns true if the map changed
boolean containsKey(K key); // returns true if key is in the map
int size(); // returns the number of key-value pairs in the map
}
Every method that takes a key throws an IllegalArgumentException if the key is null. put also throws it if the value is null.
ArrayMap<K, V> implements the Map<K, V> interface with a backing array of entries. Each entry holds one key and its value. The entries are at indices 0 through size - 1, in no particular order:
public class ArrayMap<K, V> implements Map<K, V> {
private Entry<K, V>[] arr; // the backing array
private int size; // how many pairs the map holds
private static class Entry<K, V> {
K key;
V value;
Entry(K key, V value) {
this.key = key;
this.value = value;
}
}
@SuppressWarnings("unchecked")
public ArrayMap() {
arr = (Entry<K, V>[]) new Entry[10];
size = 0;
}
@Override
public int size() {
return size;
}
// Doubles the capacity of arr and keeps the entries where they are.
private void grow() { /* already implemented */ }
// the other operations go here
}
Solution
private int indexOf(K key) {
for (int i = 0; i < size; i++) {
if (arr[i].key.equals(key)) {
return i;
}
}
return -1;
}
Solution
@Override
public V get(K key) {
if (key == null) {
throw new IllegalArgumentException();
}
int i = indexOf(key);
if (i == -1) {
return null;
}
return arr[i].value;
}
@Override
public boolean put(K key, V value) {
if (key == null || value == null) {
throw new IllegalArgumentException();
}
int i = indexOf(key);
if (i != -1) {
arr[i].value = value;
return false;
}
if (size == arr.length) {
grow();
}
arr[size] = new Entry<>(key, value);
size++;
return true;
}
Solution
@Override
public boolean remove(K key) {
if (key == null) {
throw new IllegalArgumentException();
}
int i = indexOf(key);
if (i == -1) {
return false;
}
arr[i] = arr[size - 1];
arr[size - 1] = null;
size--;
return true;
}
Solution
size is : it returns a field. get and containsKey are : each is a linear search for the key. put is : replacing a value is and appending an entry is amortized, but the search for the key comes first. remove is : moving the last entry into the gap is , but finding the entry is a linear search.
Solution
The costs are the same. size is in both, and get, containsKey, put, and remove are in both, because both find a key by linear search. They differ in memory: the array carries spare capacity, since it has more slots than entries unless it is full, and the linked list carries a next reference on every node.