Lab 09 - Classes and Objects

Instructions

Because we're introducing classes with this lab, it is no longer appropriate to use the class name ProblemX. Instead, design classes with the given specification in each problem, along with the appropriate test suite. Each method in a class must include proper Javadoc comments describing the (1) purpose of the method, (2) parameters, and (3) return value.

For problems containing multiple parts, write the methods inside the same class.

Do not round your solutions. For example, do not use Math.round or float your output to, e.g., 2 decimal places, unless specified.

What You Cannot Use

You may not use anything beyond Chapter 4.1. This includes but is not limited to:

Any violation results in a score of 0 on the lab.

Anything that trivializes a problem is disallowed. This lab is designed to get you acclimated with immutable classes and data representation. Therefore, mutation and aliasing are off the table. As such, all instance variables should be declared as final.

Please contact a staff member if you are unsure as to whether you're allowed to use something.

Notes and Version Changes:

You do not need to write tests for any hashCode implementations. We will check these manually.

Important Warning 1: You must write the method headers for all methods that we list below in order for your code to compile in the autograder. We will not award partial points for non-compiling code. So, we strongly recommend that, before you implement the methods, you first write the method headers and Javadocs.

Important Warning 2: When writing the equals method, IntelliJ will likely suggest that you use Objects.equals or ask if you want to override the method in a way that uses reflection through the getClass() method. Both of these are disallowed by the rules of the lab and problem set, and if you use them, you will earn an automatic zero. Write the method in the way that we teach you! The golden rule is that, if you don't understand an IntelliJ suggestion or a suggestion made by Google/online, don't use it!

Problems

  1. In this question, you will implement a class that represents the statistics of a list of case-sensitive words.

    1. (5 points) Design the WordStats class. It should have a single constructor that receives a List<String> of words. Rather than storing the list of words, the constructor should compute and store the frequency of each word in a private and final Map<String, Integer>. Instantiate the map as a lexicographically-ordered map.

      Hint: What are the three types of maps that you can instantiate? Which of those three orders its keys in the desired order?

    2. (10 points) This class, unlike many others that you will write in subsequent problem sets and labs, will not have any accessors. Instead, design the List<String> getListOfWords() method that returns a list of all the words in the collection.The order of the words in the list should be the same as the order of the keys in the underlying map, i.e., lexicographical order. Be aware that you should add a word to the list as many times as it occurs in the frequency map. For example, if the frequency map contains the key "hello" with value 3, then the list returned by getListOfWords() should contain three occurrences of "hello".

    3. (5 points) Design the int count() method that returns the number of words in the list.

      Warning: This method is not as simple as calling size on the map! It is a simple method to write, but it takes a bit more work.

    4. (10 points) Overload the int count(String word) method to return the number of times a given word appears in the list.

    5. (15 points) Design the Optional<String> mostFrequent() method that returns the most frequent word, or an empty Optional if there are no words. If two words share the same frequency, return the first one of that frequency.

      Hint: To create a populated Optional use Optional.of(v) for some value v. To create an empty Optional, use Optional.empty().

    6. (20 points) Design the WordStats merge(WordStats otherWord) method that merges the otherWord instance of WordStats into this instance by creating and returning a new WordStats object that contains the frequencies of both instances added together. For example, if this instance has the word "hello" with frequency 2 and the otherWord instance has the word "hello" with frequency 3, then the merged WordStats object should have the word "hello" with frequency 5.

      Warning: Do not mutate the underlying map of either this or otherWord in the implementation of the merge method. Instead, you should create a new WordStats object with the appropriate frequencies.

      Hint 1: Remember that the WordStats constructor receives a List<String> of words. How do you convert two Map<String, Integer> instances "back" into a single List<String>?

      Hint 2: Use getListOfWords...

    7. (10 points) Design the static WordStats fromString(String s) method that returns a WordStats object that represents the statistics of the words in the given string. The string is a space-separated list of words.

      Hint: You can use the String.split method to solve this problem, but a better solution would be to copy and paste your tokenize method from PSet07! If you do use String.split, the only parameter you are allowed to pass to it is the space: " ". Anything else such as "\\s+" results in an automatic zero.

      Note: Admittedly, this isn't the world's greatest use of a static method, because a better approach would be to have a constructor that receives the string.

    8. (25 points) Override the public boolean equals(Object obj) and public int hashCode() methods. Namely, two WordStats objects are equal if and only if they represent the same set of words with the same frequencies. The hash code of a WordStats object is the result of invoking Objects.hash on the underlying map.

    Files to Submit: WordStats.java, WordStatsTest.java