Overview of This Lesson
This is just a quick review of Lists and Maps.
This is just a quick review of Lists and Maps.
Recall that ArrayList
is a type of List. Lists contain objects
and can't contain primitive values. For example, a List can contain
an Integer, but not an int. Also, lists organize their elements
by a 0-based index.
Recall that when we work with Collections, or any other class with a generic type, we always replace that generic type with a concrete type. This ensures that:
ArrayList<Cat> cats = new ArrayList<Cat>();
cats.add(new Scanner(System.in));
ArrayList cats = new ArrayList();
cats.add(new Cat());
cats.add(new Cat("Arti"));
// print a cat
System.out.println( ((Cat)cats.get(0)).catInfo() );
// print all cats
for (Object ob : cats) {
Cat c = (Cat)ob;
System.out.println(c.catInfo());
}
Without generics, all objects put into a list are stored in that list
as Object
. So a programmer must always
add code to cast those objects back into whatever they were supposed
to be to begin with. When using a concrete type, the List knows it only contains cats,
so casting isn't necessary:
ArrayList<Cat> cats = new ArrayList<Cat>();
cats.add(new Cat());
cats.add(new Cat("Arti"));
// print a cat
System.out.println(cats.get(0).catInfo() );
// print all cats
for (Cat c : cats) {
System.out.println(c.catInfo());
}
Maps are similar to Lists in that they contain a list of objects, however where a List's elements are indexed with integer indexes starting at 0, a Map's indexes can be any unique object: it can be an Integer object, String object, or any other object. A Map's elements are key-value pairs, where the key is the unique object that acts as the index, and the value is the object or value associated with each index.
Maps also use generic syntax, so Map<K, V> is used to identify a Map with a generic type K for the key and V for the value.
In fact, a Map is actually a collection of Map.Entry<K, V> objects. Entry is a nested class inside the Map class, which is why we refer to it as Map.Entry. Map.Entry<K, V> models a single key-value pair. This is why we call map elements "entries" rather an "elements" or "items".
A HashMap is a special kind of Map that's very efficient when it comes to adding, removing, and searching for map elements.
When you create a HashMap, you must specify a concrete type for both the Key and the Value:
HashMap<Integer, Cat> cats = new HashMap<Integer, Cat>();
In this example, I'm using an Integer object (a boxed int) as the key and a Cat object as the value.
HashMap<String, Container> containers = new HashMap<String, Container>();
Here I'm using a String object as the key and a Container object as the value.
HashMap<Container, Inventory> shipments = new HashMap<Container, Inventory>();
Here I'm using a Container object as the key and an Inventory object as the value.
Remember that whatever you decide to use for your key, that's the entry's index, so that key must be unique. You cannot use the same key more than once in a Map.
To add elements to a map, we use the put() method:
HashMap<Integer, Cat> cats = new HashMap<>();
cats.put(1, new Cat());
To retrieve elements from a map, we use the get() method and pass the get() method the key of the element you want to get:
Cat c = cats.get("Arti");
So if you had
HashMap<String, Container> containers = new HashMap<String, Container>();
Then each Map.Entry object inside the hash map's entry set
is an instance of
Map.Entry<String, Container>
When you iterate the map's entries, you retrieve the list of entries using the
map's entrySet()
method.
The entrySet()
method actually returns
a SET of Map.Entry objects where each Map.Entry models one key-value
pair stored in the map.
So if you had a for-each loop such as:
for (Map.Entry<String, Container> element : containers.entrySet()) {
...
}
This loop says "For each key-value pair in the hashmap..."
The Map.Entry<K, V> class has a
getKey()
method: this method returns the key as
type <K>, so if you had
for (Map.Entry<String, Container> entry : containers.entrySet()) {
...
}
Then entry.getKey()
would return the String
object that's the index for each entry in the map.
Map.Entry<K, V> also has getValue()
method: this method returns the
value of the entry as type <V>, so if you had
for (Map.Entry<String, Container> entry : containers.entrySet()) {
...
}
Then entry.getValue()
would return the
Container object stored in an entry of the map.
So you could write a for-each loop that iterates through a hashmap such as:
HashMap<String, Cat> cats = new HashMap<String, Cat>();
cats.put("nomnom", new Cat());
cats.put("Foo Kitty", new Cat("Foo"));
cats.put("Arti", new Cat("Arti", "Tabby"));
for (Map.Entry<String, Cat> entry : cats.entrySet()) {
System.out.printf("Key: %s%nCat: %s%n", entry.getKey(), entry.getValue();
// note implied call to Cat's toString() when you print getValue()
}
If you need practice with maps, try this exercise:
Create a HashMap of Inventory objects where the key is the inventory item ID value and the value is the actual inventory object. You can just hard-code your map entries, unless you've got some spare time and want to create a JavaFX or Spring Boot application with an input form (which is also good practice)! Write some code to iterate through your HashMap and display each element.