Chapter 12b - Looping through Collections and Maps



Transcripted Summary

In this section, I'll show you a couple of ways to loop through Collections and Maps.

Here we have the CollectionsDemo and inside of here I have methods for each of the data structures that we've looked at.

package chapter12;

import java.util.*;

public class CollectionsDemo {

    public static void main(String[] args){
        setDemo();
        listDemo();
        queueDemo();
        mapDemo();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

So that's the Set, the List, the Queue and the Map (the same ones we looked at in the last section).

Let's go into the setDemo() method. I want to show you a couple of ways that you can loop through Collections.


Collection: Iterator

The first way is using what's called an iterator.

The Collection interface provides an iterator for you, in order to loop through a collection. For example, the Set is an unordered collection so there are no methods on Set that will allow us to get a certain item by its index, for example.

But we can use this iterator and let's just assign this to, we'll call it i. The iteratorwill allow you to go through each of the items in this collection.

In order to use it, you'll go through a loop.

public static void setDemo(){
    Set fruit = new HashSet();
    fruit.add("apple");
    fruit.add("lemon");
    fruit.add("banana");
    fruit.add("orange");
    fruit.add("lemon");

    var i = fruit.iterator();
    while(i.hasNext())
        System.out.println(i.next());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

This is a boolean expression — meaning while we have another element in the set, this will return true as long as we're not at the end of the collection, and once we've gone through all of them it will return false.

Once we're inside of the loop, let's just print them out. We can use i.next() which will return an object and it will be whatever type of object is inside of this particular collection.

Let's run this. We see that every item in this collection was printed.

That's one way to loop through a collection and this works on any collection. Let me show you another way.


Collection: Enhanced For Loop

We can use the enhanced for loop.

Every item in this Set is a String, so we can say String item, and we want to iterate over fruit.

for(String item: fruit){

}
1
2
3

Notice, I have an error here. This says incompatible types, so this is an Object, but we're saying that it's a String.



That's because Collections are generic, in that they just store an Object, and since Object is the grandparent to every class in Java, then that is safe to use.

If we wanted to be more specific, and we wanted to say that this was holding Strings, we could do that with this diamond operator, which is essentially just two angle brackets (<>).

We would make this change to the declaration line for fruit.

Set<String> fruit = new HashSet();
1

Once we do that, then this is now safe to use, and we can just go ahead and print and that way we're used to already.

for(String item : collection){
    System.out.println(item);
}
1
2
3

Collection: forEach

There's one more approach that I haven't introduced you to yet, and that's using the forEach method.

On our collection, we can do a call to forEach() and this for each will take a lambda expression.

Lambda expressions are kind of an advanced topic, we won't get into it in this course, however, I have provided a link for you in the Resource section if you'd like to learn more about it.

In the forEach(), we're going to give a generic name to whatever item we're on. It's going through, it's looping through each of these items. You give some name, you don't have to give a data type or anything like that. It's best to keep these names short because they are for this one-time use. They can't be used outside of this method call.

We'll say x and then you give an action — you do a dash, followed by a greater than sign to make an arrow and you give some action. What is it that you want this to do? I want it to print this out.

fruit.forEach(x -> System.out.println(x));
1

Running each of these shows the exact same output. It's just a matter of preference of which one that you're most comfortable with.

For this lambda expression, you can even give this a little shortcut — what they call “syntactic sugar” where you can clean this up by using shorthand.

Instead of declaring this variable and using the arrow, you can just use a System.out and then put double colon and the method that you want to use.

fruit.forEach(System.out::println);
1

What this is saying is, call this method and pass in whichever item we're on.

I prefer this way to iterate. It's a one liner, and it's nice and clean.

The others are fine as well. They're more expressive so you know what's going on. It's up to you which one you like to use.

These work for all collections so we can use this for the listDemo as well as for the queueDemo.


Map: Enhanced For Loop

Now let me show you the mapDemo() method.

public static void mapDemo(){

    Map fruitCalories = new HashMap();
    fruitCalories.put("apple", 95);
    fruitCalories.put("lemon", 20);
    fruitCalories.put("banana", 105);
    fruitCalories.put("orange", 45);
    fruitCalories.put("lemon", 17);
}
1
2
3
4
5
6
7
8
9

We can't use those exact same methods on the Map because, again, the Map is not a Collection.

To iterate over the Map we can use an enhanced for loop, but we can't just iterate over the Map itself.

We can, however, say give us the entrySet — which will give us a Set object. Once we have this Set object, then we can print out the value from the set.

for(var entry : fruitCalories.entrySet()){
    System.out.println(entry.getValue());
}
1
2
3

We used entry.getValue(), however this is red and it's saying it doesn't know what this is.



This is a Set of a Map and entry is now a Map, but again, it's type is just Object.

We can specify the specific type with the diamond operator.

For a Map you need to provide two data types:

  1. One for the key, so our key is a String.
  2. The other for the value. The value is an int.

Because Map takes two objects, we can't just specify the int as the primitive data type, we have to use the wrapper class Integer which would then be an object.

public static void mapDemo(){
    Map<String,Integer> fruitCalories = new HashMap();
    fruitCalories.put("apple", 95);
    fruitCalories.put("lemon", 20);
    fruitCalories.put("banana", 105);
    fruitCalories.put("lemon", 17);

    for(var entry : fruitCalories.entrySet()){
        System.out.println(entry.getValue());
    }
}
1
2
3
4
5
6
7
8
9
10
11

Now, notice getValue() works because it knows that this entry is a Map of a String and an Integer.

Let's run this and make sure all our values are there.

Output

105
45
95
17
1
2
3
4

Yep, those are all of our values.


Map: forEach

I want to show you one more way to iterate over a Map.

A Map also has access to the forEach() method. We can say fruitCalories.forEach() and inside of here we can pass a key and a value.

Let me put this on another line.

fruitCalories.forEach(
    (k,v)->System.out.println("Fruit: " + k + ", Calories: " + v));
1
2

I've just given it a little short-term name (k for the key, and v for the value). We don't need a data type because, again, this is only known inside of this forEach(). We give it the little dash and the arrow and then we're going to say the action is to println().

That will print this sentence for each of these. Let's see this. Voila!





Optional Independent Exercise

For your optional exercise for this chapter, the students were given a pop quiz and collectively they didn't do so great. As a result, the instructor decided to give them a makeup exam to allow them to improve their scores.



Given two Maps of test scores, which I'm going to give to you, I would like you to update the student's grades only if they did better on the makeup exam. Then print the final grades.

Let me show you the two maps that I'm providing to you.

TestResults.java

import java.util.HashMap;
import java.util.Map;

public class TestResults {

    public static Map getOriginalGrades(){

        Map grades = new HashMap();
        grades.put("Angie", 24);
        grades.put("Dave", 32);
        grades.put("Lisi", 80);
        grades.put("Raja", 50);
        grades.put("Shashi", 79);
        grades.put("Bas", 40);
        grades.put("Carlos", 59);
        grades.put("Amber", 55);
        grades.put("Rex", 95);
        grades.put("Jason", 63);
        grades.put("Nikolay", 32);

        return grades;
    }

    public static Map getMakeUpGrades(){

        Map grades = new HashMap();
        grades.put("Angie", 97);
        grades.put("Dave", 82);
        grades.put("Lisi", 76);
        grades.put("Raja", 89);
        grades.put("Shashi", 79);
        grades.put("Bas", 98);
        grades.put("Carlos", 80);
        grades.put("Amber", 95);
        grades.put("Rex", 90);
        grades.put("Jason", 62);
        grades.put("Nikolay", 79);

        return grades;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

You have two methods here. You have one to get the original grades and this returns a Map.

And these are the students, and these are their grades. Notice they didn't do so great. I got a really low score.

Then there's another method, getMakeupGgrades() that also returns a Map. And some folks did better in this one, some folks didn't.

What I would like you to do is make another class, and then you're going to make a call to these methods to get these Maps.

Then I want you to go through these Maps in your code and determine the final grade based on which one of these are higher.

  • For example, on test 1, I got a 24, on the 2nd test I got a 97. So, my final grade should be a 97.
  • However, for Lizzy she got an 80 on the 1st test, but a 76 on the 2nd one. So, she should keep her 80 score.

That's your assignment, give it a try.

Good luck.

Solution

Programming can be done many different ways, but here’s my solution.



Resources