Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Java Java Data Structures Exploring the Java Collection Framework Maps

Johannes Pedersen
Johannes Pedersen
1,006 Points

I need some help with Maps

I followed through the video, coded with Craig etc. But I haven't fully grasped maps enough to complete the following excercise.. Some pointers and hints about where to start and what not would be much appreciated.. Please dont refer me back to the video, I think i need it explained differently, maybe? :|

Would greatly appreciate your help. Thanks in advance :)

com/example/BlogPost.java
package com.example;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


public class BlogPost implements Comparable<BlogPost>, Serializable {
  private String mAuthor;
  private String mTitle;
  private String mBody;
  private String mCategory;
  private Date mCreationDate;

  public BlogPost(String author, String title, String body, String category, Date creationDate) {
    mAuthor = author;
    mTitle = title;
    mBody = body;
    mCategory = category;
    mCreationDate = creationDate;
  }

  public String getCategory() {
    return mCategory;
  }
}
com/example/Blog.java
package com.example;

import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.Map;

public class Blog {
  List<BlogPost> mPosts;


  public Map<String, Integer> getCategoryCounts(){

  }
}

4 Answers

Hi Johannes,

Let me have a go at explaining how to solve this task - hopefully that'll shed some light on the use of Maps.

First, we need to create the getCategoryCounts() method skeleton. You've done that just fine.

We then need to sort out our imports. You've added a Map - I would add a HashMap too as the Map is abstract; you can instantiate a HashMap.

At the top of the method, create a new HashMap. I called mine categoriesToCount which is probably poorly named! You've seen how to make a new HashMap - it looks like this:

HashMap<String, Integer> categoriesToCount = new HashMap<String, Integer>();

That gives us something to populate once we get going! So, what do we need to do? We want to go through all the posts in mPosts and count the number of categories. This means the map will have a key, that the category string, and a value that's the number of times that category appears in the posts. Clear so far? Shout if not!

To create a loop is easy enough. A for in loop will work fine for this. Let's get that all in one place:

  public Map<String, Integer> getCategoryCounts(){
    HashMap<String, Integer> categoriesToCount = new HashMap<String, Integer>();

    for(BlogPost post : mPosts){
      // do something with post
    }

So, we have a local variable called post that contains each post held within mPosts in turn as the loop executes.

The logic we need now is as follows: get the category in the post. If it already exists in the hashmap, add one to its value else create a new entry with a value of 1

There's two ways of doing this but I'll keep it simple with the if/else construct as the sentences above tend to make that look like the best way of doing it.

First, therefore, we want the category of the post. Referring to the BlogPost class shows us that there's a getCategory() method. Perfect! We can call post.getCategory() to get the category of the post. We next need to check if that category is already in the HashMap. There's a method called containsKey() that we can pass in the category to return a true/false boolean to say whether the key is in the map already.

The easiest bit here is to deal with the scenario where the category isn't in the Map. We just need to add it with a value of 1. The method put() lets us add a new key/value pair to the map. So, we pass in the category as the key and the number 1 as its value.

We've done a couple of things there, so let build our method. We've set up an if statement and then populated the else clause. The if condition tests whether the key returned by post.getCategory() returns true or false when passed into containsKey(). That looks like:

      if(categoriesToCount.containsKey(post.getCategory())){
       // later!
      } else {

      }

We're sorting out the scenario where the condition tests false, i.e. in the else clause. Here, we use the put() method and pass in the category and the value 1.

      if(categoriesToCount.containsKey(post.getCategory())){
       // later!
      } else {
        categoriesToCount.put(post.getCategory(), 1);
      }

OK - that's the false part done. What do we do for the true condition? We want to know how many times the category already exists in the map and add one to that. We can pass the category into the get() method. This returns the value associated with a key. That's exactly what we want! So, create a new integer variable and assign into it the value returned by get(). We can then add one to that and use the same method we did before, i.e. put() and, again, pass in the category and the number we just retrieved, plus 1.

That looks like:

      if(categoriesToCount.containsKey(post.getCategory())){
        int count = categoriesToCount.get(post.getCategory());
        categoriesToCount.put(post.getCategory(), count + 1);
      }

So that's both sides of the if clause catered for. We just need to return the map after the loop concludes. This all looks like:

  public Map<String, Integer> getCategoryCounts(){
    HashMap<String, Integer> categoriesToCount = new HashMap<String, Integer>();

    for(BlogPost post : mPosts){

      if(categoriesToCount.containsKey(post.getCategory())){
        int count = categoriesToCount.get(post.getCategory());
        categoriesToCount.put(post.getCategory(), count + 1);
      } else {
        categoriesToCount.put(post.getCategory(), 1);
      }
    } 
    return categoriesToCount;
  }

That's the method complete, and the challenge passed.

However, you may notice there's some duplication of code here. This line, sort of, appears twice:

        categoriesToCount.put(post.getCategory(), count + 1);

        categoriesToCount.put(post.getCategory(), 1);

Is there are way of making this more DRY (Don't Repeat Yourself)? Yes! And it makes the code far more brief (but far less readable!). It uses a ternary operator, i.e. an operator that takes three operands.

This could be far clearer but that's the point - I'm just putting this in here to demonstrate that fewer lines of code aren't always the way forward! Here's the new for loop with just two lines of code inside it!

    for(BlogPost post : mPosts){    
      int count = categoriesToCount.containsKey(post.getCategory()) ? categoriesToCount.get(post.getCategory()) : 0;
      categoriesToCount.put(post.getCategory(), count + 1);
    }

Here, instead of the if/else is replaced with the condition ? true : false format. Here, it's saying exactly the same as our if statement. The condition is categoriesToCount.containsKey(post.getCategory()) which we know about already. If this evaluates to true this is executed:

  categoriesToCount.get(post.getCategory())

This returns the number of times the category appears; just like we did. That result is assigned into the integer called count. If the condition evaluates to false, the zero is assigned into the count variable with : 0.

We then use the value of count to assign the integer and category into the map, just like we did before:

categoriesToCount.put(post.getCategory(), count + 1);

So we take either the integer value or zero and add one to it and put() that into the map.

Don't worry about this last bit - it just makes the code more efficient but hideously less clear!

I hope this has helped you with your maps issue.

Let me know how you get on and whether you need further explanation.

Steve.

Johannes Pedersen
Johannes Pedersen
1,006 Points

Thank you so much! This was very thorough and provided me with what i needed :)

:+1: :+1: :+1: :+1: :+1: :+1:

No problem!

corredius yarbrough
corredius yarbrough
3,167 Points

Thank you so much that was extremely valuable!!

Hi Mariusz,

Integer is a class that provides some functionality for the int data type. I didn't watch the video so I can't comment on that. But usually Integer is used when you want to use a static class method - the Java documentation will help with those methods - there are lots!

So, now "how does a string become an int"? OK - post.getCategory() returns a string object from the post instance - it has one value. The get method of Map takes a key and returns its associated value. Inside categoriesToCount the key/value pair is <String, Integer> - so you pass in the string (the key) and the get method returns the value, the integer.

So I passed the category in (I received that from post.getCategory()) and received the integer back as that's how the data is stored in the map.

This about the contents of the map. It holds the categories and the number of times they are used. The category is paired with the value:

Sharks    3
Whales    2
Nemo      0

You pass in "Sharks" to get() and you get 3 back. that's how I turned a string into an integer - it wasn't magic. And even get() can't find Nemo. :wink:

Make sense?

Steve.

Hi Steve, Super exsplanation, but I have still doubts and need more straighforward instructions :( for the following line of code:

int count = categoriesToCount.get(post.getCategory());

Why did you use int not Integer as we were presented in lesson? Moreover I can't completely get the drift with those sentences:

We can pass the category into the get() method. This returns the value associated with a key. That's exactly what we want! So, create a new integer variable and assign into it the value returned by get().

How category(string) in object mPost become number int? I know that in this HashMap<> key is string and value int, but I think I missed something important during this process.

Thank you, I got it ;)

jshell> import java.util.*;

jshell> Map<String, Integer> typesOfCar = new HashMap<String, Integer>();
typesOfCars ==> {}

jshell> typesOfCars.put("Cabrio", 1);      //first entry, not existed in dictionary before
$3 ==> null

jshell> Integer counter = typesOfCars.get("Cabrio"); //voila I have number from string
counter ==> 1

jshell> typesOfCars.put("Cabrio", ++counter);
$5 ==> 1  // (previous result in db)

jshell> typesOfCars
typesOfCars ==> {Cabrio=2} //OMG I REALLY DID IT

Good work! :+1: