简体   繁体   中英

Code to make Java HashMap Object built in processFile method accessible from main method

So I have the following program written in Java, which reads a text file's contents to a HashMap and takes in a year from the user to return what team won the World Series that year and how many times that team has won the World Series. I've successfully built the HashMap but I'm struggling with how I would make that HashMap accessible within the main driver method of the program (I understand why it's not accessible, I'm looking for how to access it). Right now, the instantiated HashMap in the main method is empty. I've been stuck on this for a while and can't seem to find a way to do this. Any insight would be greatly appreciated!

import java.io.*; // contains all classes used in file processing
import java.io.File; // allows program to gather file information
import java.io.FileNotFoundException;

import java.util.*;
import java.util.Scanner; // allows program to get user input

public class Program3 {

    public void processFile(String filename)
    {
        // pass filename as arg to File class
        File worldSeries = new File("Program3.txt");

        // Create a Scanner object
        Scanner scan;

        // Create String line variable to read each line
        String line = "";

        try {
            // HashMap which tracks each year's World Series Winner
            HashMap<Integer,String> yearWinner = new HashMap<Integer,String>();
            // HashMap which tracks the number of times each team wins the World Series
            HashMap<String,Integer> numWins = new HashMap<String,Integer>();

            // Set startYear
            int startYear = 1903;

            // Pass filename to Scanner object
            scan = new Scanner(worldSeries);

            // Check for another line in input to scan
            while(scan.hasNextLine()){
                // Skip years during which the WorldSeries was not played
                if(startYear == 1904 || startYear == 1994) {
                    startYear++;
                }
                line = scan.nextLine(); // reads nextLine of String data type
                yearWinner.put(startYear, line); //  inserts a mapping into a HashMap
                if(numWins.containsKey(line)) { // checks if a key is mapped into the HashMap or not
                    numWins.put(line, numWins.get(line)+1); // if key exists, increment numWins value
                }else {
                    numWins.put(line,1); // if key does not exist, set numWins to 1
                }
                startYear++; // increment year
            }
            //testing for some case
            System.out.println(yearWinner.get(1905));
            System.out.println(numWins.get("New York Yankees"));
        } catch (FileNotFoundException e) {
            System.out.println("File not found");
        }
    }
    /*
    collectInput - method to collect user input for the year.
     */
    public int collectInput() {
        Scanner in = new Scanner(System.in); // Scanner object to read user input.

        int year; // declare year
        System.out.print("Please enter a year between 1903 & 2020: ");
        year = in.nextInt(); // collect the user input

        // check the year is either 0(sentinel value) or within range 1903 and 2020.
        while (year != 0 && (year < 1903 || year > 2020)) {
            // otherwise collect a valid year.
            System.out.print("Year is invalid! Please enter a year between 1903 & 2020: ");
            year = in.nextInt();
        }

        return year; // return the year.
    }
    /*
    displayOutput - method gets the user input and displays the output.
    @param winners - class instance
    @param yearWinner - map which stores year and team
    @param winsMap - map which stores team and number of wins
     */
    public void displayOutput(Program3 winners, HashMap<Integer, String> yearWinner, HashMap<String, Integer> numWins) {

        // loop the input until sentinel value is entered
        while (true) {

            // call the method to collect input from user
            int year = collectInput();

            // validate for sentinel value
            if (year == 0) {
                System.out.println("Thank you, goodbye!");
                return; // end program
            }

            // get the winning team for the given year.
            String winningTeam = winners.getWinningTeamForYear(year, yearWinner);

            // if there is winning team for the given year..
            if (winningTeam != null) {
                // get the number of wins of that team between 1903 and 2020.
                int numOfWins = winners.getNumberOfWins(winningTeam, numWins);
                // display the winning team and number of wins.
                System.out.println("The winning for the Year " + year + " is: " + winningTeam);
                System.out.println("Number of Wins secured by team: " + winningTeam + " is " + numOfWins);
            }
        }
    }
    // pass the year and get the winning team
    public String getWinningTeamForYear(int year, HashMap<Integer, String> yearWinner) {
        // if there is an entry for the given year.. to validate excluded years
        if (yearWinner.containsKey(year)) { // if yearWinner containsKey
            return yearWinner.get(year); // return the winning team
        }
        else {
            // display the message and return null.
            System.out.println("World Series not held in " + year);
            return null;
        }
    }

    // pass the team name in the hashmap and get the number of wins
    public int getNumberOfWins(String winningTeam, HashMap<String, Integer> numWins) {
        return numWins.get(winningTeam);
    }

    public static void main(String[] args) {

        HashMap<Integer, String> yearWinner = new HashMap<>(); // hashmap to store year and winners.
        HashMap<String, Integer> numWins = new HashMap<>(); // hashmap to store winners and number of wins.

        String fileName = "Program3.txt"; // file name where the data is stored.
        // You can also use full path of file Ex. E:\\InputFiles\\Program3.txt (\\ delimiter slash indicating \)

        Program3 winners = new Program3();
        winners.processFile("Program3.txt");
        System.out.println(yearWinner);
        winners.displayOutput(winners, yearWinner, numWins); // non static method to collect user input and display output.

    }
}

Program3.txt from which HashMap is being built:

Boston Americans
New York Giants
Chicago White Sox
Chicago Cubs
Chicago Cubs
Pittsburgh Pirates
Philadelphia Athletics
Philadelphia Athletics
Boston Red Sox
Philadelphia Athletics
Boston Braves
Boston Red Sox
Boston Red Sox
Chicago White Sox
Boston Red Sox
Cincinnati Reds
Cleveland Indians
New York Giants
New York Giants
New York Yankees
Washington Senators
Pittsburgh Pirates
St. Louis Cardinals
New York Yankees
New York Yankees
Philadelphia Athletics
Philadelphia Athletics
St. Louis Cardinals
New York Yankees
New York Giants
St. Louis Cardinals
Detroit Tigers
New York Yankees
New York Yankees
New York Yankees
New York Yankees
Cincinnati Reds
New York Yankees
St. Louis Cardinals
New York Yankees
St. Louis Cardinals
Detroit Tigers
St. Louis Cardinals
New York Yankees
Cleveland Indians
New York Yankees
New York Yankees
New York Yankees
New York Yankees
New York Yankees
New York Giants
Brooklyn Dodgers
New York Yankees
Milwaukee Braves
New York Yankees
Los Angeles Dodgers
Pittsburgh Pirates
New York Yankees
New York Yankees
Los Angeles Dodgers
St. Louis Cardinals
Los Angeles Dodgers
Baltimore Orioles
St. Louis Cardinals
Detroit Tigers
New York Mets
Baltimore Orioles
Pittsburgh Pirates
Oakland Athletics
Oakland Athletics
Oakland Athletics
Cincinnati Reds
Cincinnati Reds
New York Yankees
New York Yankees
Pittsburgh Pirates
Philadelphia Phillies
Los Angeles Dodgers
St. Louis Cardinals
Baltimore Orioles
Detroit Tigers
Kansas City Royals
New York Mets
Minnesota Twins
Los Angeles Dodgers
Oakland Athletics
Cincinnati Reds
Minnesota Twins
Toronto Blue Jays
Toronto Blue Jays
Atlanta Braves
New York Yankees
Florida Marlins
New York Yankees
New York Yankees
New York Yankees
Arizona Diamondbacks
Anaheim Angels
Florida Marlins
Boston Red Sox
Chicago White Sox
St. Louis Cardinals
Boston Red Sox
Philadelphia Phillies
New York Yankees
San Francisco Giants
St. Louis Cardinals
San Francisco Giants
Boston Red Sox
San Francisco Giants
Kansas City Royals
Chicago Cubs
Houston Astros
Boston Red Sox
Washington Nationals
Los Angeles Dodgers

Add the two maps as parameters to the processFile() method.

Parameters of a collection type that are passed in empty, and expected to be filled by the method, are referred to as result collectors .

By using parameters instead of return value, you can have more than one.

public void processFile(String filename, HashMap<Integer,String> yearWinner,
                        HashMap<String,Integer> numWins)
{
    ... code unchanged ...

    try {
        // delete the lines that declared yearWinner and numWins

        // Set startYear
        int startYear = 1903;

        ... code unchanged ...
public static void main(String[] args) {

    ... code unchanged ...

    winners.processFile("Program3.txt", yearWinner, numWins);

    ... code unchanged ...
}

The Answer by Andreas is correct and smart. But there is a downside to passing such result collector arguments. The fact that they are being used to communicate back to the calling method is not obvious.

In Java 16 and later, we can make more obvious the results by returning a record that holds both of the generated maps.

Records

The records feature lets us briefly declare a class whose primary purpose is to transparently and immutable communicate data. Simply declare the type and name of the member fields within parentheses. The compiler implicitly creates the constructor, getters, equals & hashCode , and toString .

Be aware that you can declare your record in a separate .java file, nested within a class, or even locally within a method.

record Results ( Map<Integer,String> yearWinner, Map<String,Integer> numWins ) 
{
}

Notice that we can promise to return the more general Map interface rather than specific concrete HashMap implementation. This polymorphic encapsulation gives us the freedom to later change our choice of Map implementation.

The getter methods are named the same as the property (member field) they export. The JavaBeans-style naming with get… / is… is not used.

System.out.println( myResults.yearWinner() ) ;

Change your processing method to return an object of this record type.

public Results processFile( String filename )  // Returns a `Results` object. 
{
    …
    HashMap<Integer,String> yearWinner = … ;
    HashMap<String,Integer> numWins = … ;
    return new Results( yearWinner , numWins ) ;
}

Better to return unmodifiable maps . Call Map.copyOf in Java 10 and later. A record by definition is meant to immutably communicate data. So make your record as deeply immutable as is practicable.

public Results processFile( String filename )
{
    …
    HashMap<Integer,String> yearWinner = … ;
    HashMap<String,Integer> numWins = … ;
    return new Results( Map.copyOf( yearWinner ) , Map.copyOf( numWins ) ) ;  // Return immutable maps, copied from our mutable maps.
}

Of course in older versions of Java, you could have declared a class to do the same thing (return results to calling method). But declaring a conventional class involves so much boilerplate that programmers often do not bother. The new records feature was expressly developed to address this kind of need to merely communicate immutable data. A nice bonus benefit is a huge reduction in the boilerplate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM