Counting Words with a Map
Sorting WordData objects by frequency in descending order means placing the record with the highest count first.
Predicting the First Word in the Ranking
A word-counting program can tell you how many times each word appears in a text. The useful question is usually which words appear most often. This article follows one step of that program: the sort that arranges the word records so the highest count comes first. You will commit to a prediction before seeing the mechanism that produces the result.
Suppose the sample data holds three records. The word the has a count of 3, the word is has a count of 7, and the word code has a count of 5. Using the rule that the highest count should come first, decide which word should print first and which should print last. Write down your answer before you read on.
What do you think happens?
With descending order by count, which word prints first, and which prints last?
Reveal answer
is first, the last. The order is is (7), code (5), the (3).
The descending rule places the record with the highest count at the front. Since is has the highest count of 7, it comes first. Code has 5, so it sits in the middle. The has 3, the lowest, so it comes last.
Why Records Need an Order
A word-counting program produces one record per distinct word, and each record holds that word's frequency. Those records are only useful when they can be ordered. Each record has two pieces of information that play different roles. The word text identifies the record, while the count attribute is what the sort uses to arrange the records.
In this program, the count field acts as the sort key. The sort does not look at the word text when it decides order. It looks at each record's count and places records with higher counts ahead of records with lower counts, so the record with the highest count ends up first.
The Three Parts of the Sort Line
The central line of the source is a single statement. It copies the values of the words collection into an ArrayList and then sorts that list, using a lambda expression as the comparator. Read the statement as a whole before reading its pieces. The statement has three parts that work together: the copy into a list, the call to Collections.sort, and the comparator passed as the second argument.
- Line 1 copies the values of the words Map into an
ArrayList - Line 2 sorts that list using the lambda as the comparator
The sort cannot reorder a Map directly in the way a list can be reordered, so the values are first copied into an ArrayList. The sort then receives the list and the comparator. Each part has a clear job, and a failure can come from any one of them.
In the before list, the record for is sits in the middle position. After the descending sort, it moves to the front because its count of 7 is the highest. The record for the, with the lowest count, moves to the end.
How the Comparator Decides Order
The sort needs a way to compare two WordData objects. The lambda expression (a,b) -> b.count - a.count supplies that rule. The two parameters a and b stand for two WordData objects that the sort wants to compare. The body reads the count attribute of each one and subtracts a's count from b's count.
Tracing the Comparator on a Pair
Comparing the is record with the the record
The list holds the record for the (count 3) and the record for is (count 7). Decide which record the comparator places first.
- 1Assign the parameters
Pass the record for the as a and the record for is as b, so
a.countis 3 andb.countis 7. - 2Evaluate the body
Compute
b.countminusa.count, which is 7 minus 3, giving 4. - 3Read the sign
The result is positive, so a belongs after b. The record for is comes before the record for the.
The is record is placed ahead of the the record in the descending list.
The comparator handles one pair at a time. Once you can trace one pair and see which record it places first, you can apply the same method to any two records in the list. This is the step to check when a sorted output does not match what you expected.
Mistakes That Flip or Break the Order
Common mistake Swapping the operands of the subtraction
What it looks likeWriting (a,b) -> a.count - b.count in place of (a,b) -> b.count - a.countWhy it failsThe source states that swapping the operands reverses the result, so the sort produces ascending order instead of descending order.
Do this insteadKeep b first in the subtraction when you want the highest count to come first.
Common mistake Sorting the Map itself and expecting the records to reorder
What it looks likeCalling Collections.sort directly on the words Map
Why it failsThe source explains that a Map cannot be reordered in the way a list can, which is why the values are first copied into an
ArrayList.Do this insteadCopy words.values() into an ArrayList, then sort that list with the comparator.
Common mistake Assuming a wrong output is caused by the sort call alone
Why it failsThe source notes that the three parts of the statement, the copy, the sort call, and the comparator, each have a job, and a failure can come from any one of them.
Do this insteadCheck the list before the sort, then the comparator on individual pairs, then the list after the sort call.
Practice Predicting Descending Order
Four WordData records hold these counts: data with count 2, sort with count 9, list with count 4, and map with count 6. Predict the order in which the words print after Collections.sort with the comparator (a,b) -> b.count - a.count. Then trace the comparator on the pair list and map to confirm which one comes first.
Hints
- The descending rule puts the highest count first.
- Compare the pair list (4) and map (6) by computing
b.countminusa.countwith a as list and b as map. - The final order should run from the largest count down to the smallest.
Key Takeaways
- A word-counting program sorts WordData objects by the count field, so the record with the highest frequency appears first.
- The line copies
words.values()into anArrayListand sorts it withCollections.sortusing the lambda (a,b) ->b.count-a.count. - Writing
b.countfirst in the subtraction produces descending order, and swapping the operands reverses the result to ascending. - When output does not match expectations, trace the comparator on individual pairs and check the list before and after the sort call.