slanted W3C logo

Summary: Example Problem

Examples covering inheritance, collections, and exceptions.

Example Problem

Write a program that counts the number of gold, silver, and bronze medals won by each country in the Olympic Games. Your program should accept accept input from the keyboard until the user types the sentinel string "done" (alternatively, you can read from a file because the input is very long).

Assume that the input is line based, with the results for an event given as a comma-delimited list of fields:

men's curling, Canada, Finland, USA

where the first field is the event name, the second field is the gold medal winning country, the third field is the silver medal winning country, and the fourth field is the bronze medal winning country (note that this problem ignores the case where a tie has occurred). If a field is missing from the input, your program should print a warning message and ignore the line of input.

Your program should output the medal count for each country in order of the number of gold medals won. If there is tie for the number of gold medals won, the countries that tied should be listed in alphabetic order.

Sample Input: (based on the Turin 2006 Winter Olympics)

men's curling, Canada, Finland, USA
women's curling, Sweden, Switzerland, Canada
men's ice hockey, Sweden, Finland, Czech Republic
women's ice hockey, Canada, Sweden, USA
men's skeleton, Canada, Canada, Switzerland
women's skeleton, Switzerland, Great Britain, Canada
men's luge, Italy, Russia, Latvia
women's luge, Germany, Germany, Germany
mixed luge, Austria, Germany, Italy
men's 4 x 7.5 km relay biathlon, Germany, Russia, France
men's 12.5 km pursuit biathlon, France, Norway, Germany
men's 20 km individual biathlon, Germany, Norway, Norway
women's 4 x 6 km relay biathlon, Russia, Germany, France
women's 10 km pursuit biathlon, Germany, Germany, Russia
women's 15 km individual biathlon, Russia, Germany, Russia
men's halfpipe snowboarding, USA, USA, Finland
women's halfpipe snowboarding, USA, USA, Norway
some made up event, Canada, Bosnia and Herzegovina, Serbia and Montenegro
done

Sample Output:
Your program should output the country name using 25 spaces, and each number of medals using 3 spaces.

                   Canada  4  1  2
                  Germany  4  5  2
                   Russia  2  2  2
                   Sweden  2  1  0
                      USA  2  2  2
                  Austria  1  0  0
                   France  1  0  2
                    Italy  1  0  1
              Switzerland  1  1  1
   Bosnia and Herzegovina  0  1  0
           Czech Republic  0  0  1
                  Finland  0  2  1
            Great Britain  0  1  0
                   Latvia  0  0  1
                   Norway  0  2  2
    Serbia and Montenegro  0  0  1

Solution Using Maps

Perhaps the most straightforward way of solving the problem is to use 3 maps; one that maps country names to gold medal counts, one that maps country names to silver medal counts, and one that maps country names to bronze medal counts. If you use TreeMap for each map, you can solve half of the output problem because the country names will be sorted alphabetically for you.

One thing to watch out for is to make sure a country that wins a medal shows up in all of the maps. For example, in the example input, Latvia won only a bronze medal, but Latvia should show up in the gold medal map (with 0 gold medals won) and the silver medal map (with 0 silver medals won) in addition to the bronze medal map (with 1 bronze medal won). If you forget to do this, outputting the results becomes even more complicated.

import java.util.*;
import java.io.*;

public class MedalCount1
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      Map<String, Integer> goldMedals = new TreeMap<String, Integer>();
      Map<String, Integer> silverMedals = new TreeMap<String, Integer>();
      Map<String, Integer> bronzeMedals = new TreeMap<String, Integer>();
      
      String lineIn = input.nextLine();
      while (!lineIn.equals("done"))
      {
         try
         {
            StringTokenizer tok = new StringTokenizer(lineIn, ",");
            
            // tok will throw an exception on nextToken if there are
            // missing fields; catch the NoSuchElementException to
            // solve the input validation problem
            
            String event = tok.nextToken().trim();
            
            // gold, silver, and bronze winning countries
            String goldCountry = tok.nextToken().trim();
            String silverCountry = tok.nextToken().trim();
            String bronzeCountry = tok.nextToken().trim();
            
            // Update gold medal country by adding 1 to their
            // gold medal counts; also, add the country to
            // the silverMedals and bronzeMedals maps if necessary
            Integer oldGold = goldMedals.get(goldCountry);
            if (oldGold == null)
            {
               oldGold = 0;
            }
            goldMedals.put(goldCountry, oldGold + 1);
            if (silverMedals.get(goldCountry) == null)
            {
               silverMedals.put(goldCountry, 0);
            }
            if (bronzeMedals.get(goldCountry) == null)
            {
               bronzeMedals.put(goldCountry, 0);
            }
            
            // Update silver medal country by adding 1 to their
            // silver medal counts; also, add the country to
            // the goldMedals and bronzeMedals maps if necessary
            Integer oldSilver = silverMedals.get(silverCountry);
            if (oldSilver == null)
            {
               oldSilver = 0;
            }
            silverMedals.put(silverCountry, oldSilver + 1);
            if (goldMedals.get(silverCountry) == null)
            {
               goldMedals.put(silverCountry, 0);
            }
            if (bronzeMedals.get(silverCountry) == null)
            {
               bronzeMedals.put(silverCountry, 0);
            }
            
            // Update bronze medal country by adding 1 to their
            // bronze medal counts; also, add the country to
            // the goldMedals and silverMedals maps if necessary
            Integer oldBronze = bronzeMedals.get(bronzeCountry);
            if (oldBronze == null)
            {
               oldBronze = 0;
            }
            bronzeMedals.put(bronzeCountry, oldBronze + 1);
            if (goldMedals.get(bronzeCountry) == null)
            {
               goldMedals.put(bronzeCountry, 0);
            }
            if (silverMedals.get(bronzeCountry) == null)
            {
               silverMedals.put(bronzeCountry, 0);
            }
         }
         catch (NoSuchElementException ex)
         {
            output.println("\tMissing field on input!");
         }
         lineIn = input.nextLine();
      }
      
      // Gold medal counts:
      
      // Find the unique gold medal counts by putting the goldMedal
      // values into a TreeSet
      TreeSet<Integer> ascendingGoldCounts = new TreeSet<Integer>(goldMedals.values());
      
      // Reverse the set so that we get the unique gold medal counts
      // in descending order (from largest to smallest) 
      Set<Integer> goldCounts = ascendingGoldCounts.descendingSet();
      
      // For each number of gold medals won (from largest to smallest)
      for (Integer numGold : goldCounts)
      {
         // Find the countries that won numGold
         for (String country : goldMedals.keySet())
         {
            Integer thisGold = goldMedals.get(country);
            if (thisGold.equals(numGold))
            {
               Integer thisSilver = silverMedals.get(country);
               Integer thisBronze = bronzeMedals.get(country);
               output.printf("%25s %2s %2s %2s%n",
                  country,
                  thisGold.toString(),
                  thisSilver.toString(),
                  thisBronze.toString());
            }
         }
      }
   }
}

Solution Using One Map

Instead of using three maps, you can use one map that maps country names to a list of the number of gold, silver, and bronze medals won.

import java.util.*;
import java.io.*;

public class MedalCount2
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      Map<String, List<Integer>> medals =
         new TreeMap<String, List<Integer>>();
      
      String lineIn = input.nextLine();
      while (!lineIn.equals("done"))
      {
         try
         {
            StringTokenizer tok = new StringTokenizer(lineIn, ",");
            
            // tok will throw an exception on nextToken if there are
            // missing fields; catch the NoSuchElementException to
            // solve the input validation problem

            String event = tok.nextToken().trim();
            
            // gold, silver, and bronze winning countries
            String country1 = tok.nextToken().trim();
            String country2 = tok.nextToken().trim();
            String country3 = tok.nextToken().trim();
            
            // update the gold medal winning country
            List<Integer> medals1 = medals.get(country1);
            if (medals1 == null)
            {
               medals1 = new ArrayList<Integer>();
               medals1.add(1);
               medals1.add(0);
               medals1.add(0);
            }
            else
            {
               int numGold = medals1.get(0);
               medals1.set(0, numGold + 1);
            }
            medals.put(country1, medals1);
            
            // update the silver medal winning country
            List<Integer> medals2 = medals.get(country2);
            if (medals2 == null)
            {
               medals2 = new ArrayList<Integer>();
               medals2.add(0);
               medals2.add(1);
               medals2.add(0);
            }
            else
            {
               int numSilver = medals2.get(1);
               medals2.set(1, numSilver + 1);
            }
            medals.put(country2, medals2);
            
            // update the bronze medal winning country
            List<Integer> medals3 = medals.get(country3);
            if (medals3 == null)
            {
               medals3 = new ArrayList<Integer>();
               medals3.add(0);
               medals3.add(0);
               medals3.add(1);
            }
            else
            {
               int numBronze = medals3.get(2);
               medals3.set(2, numBronze + 1);
            }
            medals.put(country3, medals3);
         }
         catch (NoSuchElementException ex)
         {
            output.println("\tMissing field on input!");
         }
         lineIn = input.nextLine();
      }
      
      // Gold medal counts:
      
      // Find the unique gold medal counts by putting the number
      // of gold medals won by each country into a TreeSet
      TreeSet<Integer> ascendingGoldCounts = new TreeSet<Integer>();
      for (List<Integer> countryMedalCount : medals.values())
      {
         ascendingGoldCounts.add(countryMedalCount.get(0));
      }
      
      // Reverse the set so that we get the unique gold medal counts
      // in descending order (from largest to smallest) 
      Set<Integer> goldCounts = ascendingGoldCounts.descendingSet();
      
      // For each number of gold medals won (from largest to smallest)
      for (Integer numGold : goldCounts)
      {
         // Find the countries that won numGold
         for (String country : medals.keySet())
         {
            Integer thisGold = medals.get(country).get(0);
            if (thisGold.equals(numGold))
            {
               Integer thisSilver = medals.get(country).get(1);
               Integer thisBronze = medals.get(country).get(2);
               
               output.printf("%25s %2s %2s %2s%n",
                  country,
                  thisGold.toString(),
                  thisSilver.toString(),
                  thisBronze.toString());
            }
         }
      }
   }
}

Solution Using Four Lists

You could also use four parallel lists, one for the countries, and one each for the number of gold, silver, and bronze medals won. The important detail to watch out for is that you make sure you update the lists so that they remain synchronized with each other: the element at index 0 in the gold medal list should be the number of gold medals won by the country at element 0 in the country list (and similarly for the silver and bronze metal lists).

Outputting the results is slightly harder because the list of countries cannot be sorted without destroying its relationship to the medal lists.

import java.util.*;
import java.io.*;

public class MedalCount3
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      List<String> country = new ArrayList<String>();
      List<Integer> gold = new ArrayList<Integer>();
      List<Integer> silver = new ArrayList<Integer>();
      List<Integer> bronze = new ArrayList<Integer>();
      
      String lineIn = input.nextLine();
      while (!lineIn.equals("done"))
      {
         try
         {
            StringTokenizer tok = new StringTokenizer(lineIn, ",");
            String event = tok.nextToken().trim();
            
            // tok will throw an exception on nextToken if there are
            // missing fields; catch the NoSuchElementException to
            // solve the input validation problem
            
            // gold, silver, and bronze winning countries
            String country1 = tok.nextToken().trim();
            String country2 = tok.nextToken().trim();
            String country3 = tok.nextToken().trim();
            
            // update the gold medal winning country
            int index = country.indexOf(country1);
            if (index == -1)
            {
               country.add(country1);
               gold.add(1);
               silver.add(0);
               bronze.add(0);
            }
            else
            {
               int numGold = gold.get(index);
               gold.set(index, numGold + 1);
            }
            
            // update the silver medal winning country
            index = country.indexOf(country2);
            if (index == -1)
            {
               country.add(country2);
               gold.add(0);
               silver.add(1);
               bronze.add(0);
            }
            else
            {
               int numSilver = silver.get(index);
               silver.set(index, numSilver + 1);
            }
            
            // update the bronze medal winning country
            index = country.indexOf(country3);
            if (index == -1)
            {
               country.add(country3);
               gold.add(0);
               silver.add(0);
               bronze.add(1);
            }
            else
            {
               int numBronze = bronze.get(index);
               bronze.set(index, numBronze + 1);
            }
         }
         catch (NoSuchElementException ex)
         {
            output.println("\tMissing field on input!");
         }
         lineIn = input.nextLine();
      }
      
      // gold medal counts
      TreeSet<Integer> ascendingGoldCounts = new TreeSet<Integer>(gold);
      Set<Integer> goldCounts = ascendingGoldCounts.descendingSet();
      
      // For each number of gold medals won (from largest to smallest)
      for (Integer numGold : goldCounts)
      {
         Set<String> result = new TreeSet<String>();
         
         // Find the countries that won numGold
         for (int i = 0; i < country.size(); i++)
         {
            Integer thisGold = gold.get(i);
            if (thisGold.equals(numGold))
            {
               Integer thisSilver = silver.get(i);
               Integer thisBronze = bronze.get(i);
               
               String thisResult =
                  String.format("%s %2s %2s %2s",
                                country.get(i),
                                thisGold.toString(),
                                thisSilver.toString(),
                                thisBronze.toString());
               result.add(thisResult);
            }
         }
         for (String s : result)
         {
            output.printf("%34s%n", s);
         }
      }
   }
}