16
\$\begingroup\$

Instructions:

Write a Java program the displays the State bird and flower. You should use your IDE for this exercise. The program should prompt the user to enter a state and print both the state bird and flower. The user should be able to enter a State without worrying about case. (e.g. Users could enter Maryland, maryland, MARYLAND or any other possible combination of lower and upper case characters. States may also contain leading and trailing white spaces. Hint: Store the state information in a multi-dimensional array. The program should continue to prompt the user to enter a state until None is entered. You will need to do some research to find the state birds and flowers.


import java.util.Scanner;

public class StateInfo {

    public static int getInfo(String stateInfo[][],String state)
   {
       int position = -1;
       boolean found = false;
       for (int index=0; index<stateInfo.length && !found; index++)
       {
           if(stateInfo[index][0].equalsIgnoreCase(state))
               position=index;              
       }
       return position;
   }



    public static void main(String[] args) {

        Scanner userInput = new Scanner(System.in);

        String[][] stateInformation = new String[][] {
            {"Alabama", "Yellowhammer", "Camelia"},
            {"Alaska", "Willow Ptarmigan", "Forget-Me-Not"},
            {"Arizona", "Cactus Wren", "Saguaro Cactus Blossom"},
            {"Arkansas", "Mockingbird", "Apple Blossom"},
            {"California", "California Valley Quail", "Golden Poppy"},
            {"Colorado", "Lark Bunting", "Rocky Mountain Columbine"},
            {"Connecticut", "Robin", "Mountain Laurel"},
            {"Delaware", "Blue Hen Chicken", "Peach Blossom"},
            {"Florida", "Mockingbird", "Orange Blossom"},
            {"Georgia", "Brown Thrasher", "Cherokee Rose"},
            {"Hawaii", "Nene", "Hawaiian Hibiscus"},
            {"Idaho", "Mountain Bluebird", "Syringa, mock orange"},
            {"Illinois", "Cardinal", "Violet"},
            {"Indiana", "Cardinal", "Peony"},
            {"Iowa", "Eastern Goldfinch", "Wild Praire Rose"},
            {"Kansas", "Western Meadowlark", "Sunflower"},
            {"Kentucky", "Cardinal", "Goldenrod"},           
            {"Louisiana", "Eastern Brown Pelican", "Magnolia"},
            {"Maine", "Chickadee", "Pine Cone and Tassel"},
            {"Maryland", "Baltimore Oriole", "Black-Eyed Susan"},
            {"Massachusetts", "Chickadee", "Mayflower"},
            {"Michigan", "Robin", "Apple Blossom"},
            {"Minnesota", "Common Loon", "Pink and White Lady's Slippper"},
            {"Mississippi", "Mockingbird", "Magnolia"},
            {"Missouri", "Bluebird", "Hawthorn"},
            {"Montana", "Western Meadowlark", "Bitterroot"},
            {"Nebraska", "Western Meadowlark", "Goldenrod"},
            {"Nevada", "Mountain Bluebird", "Sagebrush"},
            {"New Hampshire", "Purple Finch", "Purple Lilac"},
            {"New Jersey", "Eastern Goldfinch", "Violet"},
            {"New Mexico", "Roadrunner", "Yucca Flower"},
            {"New York", "Bluebird", "Rose"},
            {"North Carolina", "Cardinal", "Flowering Dogwood"},
            {"North Dakota", "Western Meadowlark", "Wild Praire Rose"},
            {"Ohio", "Cardinal", "Scarlet Carnation"},
            {"Oklahoma","Scissor-tailed Flycatcher","Oklahoma Rose"},            
            {"Oregon", "Western Meadowlark", "Oregon Grape"},
            {"Pennsylvania", "Ruffed Grouse", "Mountain Laurel"},
            {"Rhode Island", "Rhode Island Red", "Violet"},           
            {"South Carolina", "Great Carolina Wren", "Yellow Jessamine"},
            {"South Dakota", "Ring-necked Pheasant", "Pasque Flower"},
            {"Tennessee", "Mockingbird", "Purple Passionflower"},
            {"Texas", "Mockingbird", "Bluebonnet Sp."},
            {"Utah", "Common American Gull", "Sego Lily"},
            {"Vermont", "Hermit Thrush", "Red Clover"},
            {"Virginia","Cardinal"," American Dogwood"},
            {"Washington", "Willow Goldfinch", "Coast Rhododendrum"},
            {"West Virginia", "Cardinal", "Rhododendron"},
            {"Wisconsin", "Robin", "Wood Violet"},
            {"Wyoming", "Western Meadowlark", "Indian Paintbrush"}
        };

        while(true) {
            System.out.println("Enter a State or None to exit:");
            String stateName = userInput.next();

            if(stateName.equalsIgnoreCase("None")) {
                System.exit(0);
            }
            else {
                int position = getInfo(stateInformation, stateName);
                if(position != -1) {
                    System.out.println("Bird: " + stateInformation[position][1]);
                    System.out.println("Flower: " + stateInformation[position][2]);                   
                }
                else {
                    System.out.println("Invalid State Entered");
                }                
            }            
        }
    }
}
\$\endgroup\$
1
  • 3
    \$\begingroup\$ Be sure to read this: How to Ask \$\endgroup\$
    – Jamal
    Jul 4, 2015 at 16:28

8 Answers 8

15
\$\begingroup\$

Instead of using a HashMap as EthanBierlein recommended, a more object oriented way to handle the state information would be to setup a class that stores the state bird and the state flower.

Here is what I mean:

public class State {
    private final String flower;
    private final String bird;

    public State(String flower, String bird) {
        this.flower = flower;
        this.bird = bird;
    }

    private String getFlower() {
        return this.flower;
    }
    private String getBird() {
        return this.bird;
    }
}

Notice how there is no name property? Keep reading to find out why.


Now, the easiest way to go through and find the correct state would be to stick all the states in to an array. However, if you instead use a Map, later, when you are retrieving States from the Map, your code will be faster. I'll go more into that later.

Here is what I mean:

Map<String, State> states = new HashMap<>();
states.put("Alabama", new State("Yellowhammer", "Camelia");
...

Here comes the easiest part. Now all you have to do is pass the state string given to you through user input to Map.get; no iteration needed.

Here is the code:

if( !(state = states.get(stateName)) ) { // search through the states map by state name
    System.out.println("Invalid State Entered");
    continue; // go back to the top of the loop
}
System.out.printf("Bird: %s%nFlower: %s%n", state.getBird(), state.getFlower());

By using a map and then using the Map.get method, you are speeding up your code because this operation runs in O(1) time, rather than iterating through an array of States and checking a name property.

Map recommendation and speed information provided by Simon André Forsberg.

\$\endgroup\$
9
  • 2
    \$\begingroup\$ A Map<String, State> would be useful, looping and checking for state.getName().equals(stateName) is an O(n) operation, map.get(stateName) is O(1) \$\endgroup\$ Jul 4, 2015 at 17:18
  • \$\begingroup\$ @SimonAndréForsberg I will change it. \$\endgroup\$
    – SirPython
    Jul 4, 2015 at 17:23
  • 1
    \$\begingroup\$ @SimonAndréForsberg The O(n) vs O(1) thing does not matter here. n is a constant, so O(n) = O(1). Unless you benchmarked these two approaches, I'm not sure this advice is relevant. With a small, fixed number of states, the locality of a contiguous array could very well prove more important for performance than the complexity of the underlying algorithm. In general, knowing about hashmaps is a good thing, though, and not reinventing the wheel by hand-coding a traversal is a good thing too. But in this case, the performance aspect is far from obvious. \$\endgroup\$
    – Clément
    Jul 4, 2015 at 18:58
  • \$\begingroup\$ I think that an enum would be better than a general class to store this information, since it is a constant that will not be modified at runtime. \$\endgroup\$ Jul 4, 2015 at 20:51
  • 1
    \$\begingroup\$ @SirPython The properties are, true, but you could create another State instance during runtime, which doesn't really make sense. \$\endgroup\$ Jul 4, 2015 at 21:02
11
\$\begingroup\$

Unused variables

In getInfo, the found variable is unused.

Inefficient algorithm

The getInfo method iterates over all entries in stateInfo, even after it found a match. It could return immediately, and without using the position local variable:

public static int getInfo(String stateInfo[][], String state) {
    for (int index = 0; index < stateInfo.length; index++) {
        if (stateInfo[index][0].equalsIgnoreCase(state)) {
            return index;
        }
    }
    return -1;
}

The other inefficiency is that looking up a state by iterating over a list: for example to find "Wyoming", the program has to step through all previous elements. If you use a Map instead of an array, then lookups can be instantaneous or close to that.

Avoid System.exit

Exiting in the middle of the program with System.exit is not pretty. It's better to let the program terminate by itself by finishing it's main execution loops and relinquishing control. In this example, you could replace System.exit(0); with break; for that effect.

Coding style

This code doesn't follow the common coding style:

public static int getInfo(String stateInfo[][],String state)
{
    int position = -1;
    boolean found = false;
    for (int index=0; index<stateInfo.length && !found; index++)
    {
        if(stateInfo[index][0].equalsIgnoreCase(state))
            position=index;
    }
    return position;
}

Reformatting in an IDE automatically turns it into:

public static int getInfo(String stateInfo[][], String state) {
    int position = -1;
    boolean found = false;
    for (int index = 0; index < stateInfo.length && !found; index++) {
        if (stateInfo[index][0].equalsIgnoreCase(state)) {
            position = index;
        }
    }
    return position;
}

Suggested implementation

With the above improvements, the class becomes:

public class StateInfo {

    private final String name;
    private final String bird;
    private final String flower;

    private static final Map<String, StateInfo> states = new HashMap<>();

    static {
        String[][] stateInformation = new String[][] {
            {"Alabama", "Yellowhammer", "Camelia"},
            {"Alaska", "Willow Ptarmigan", "Forget-Me-Not"},
            {"Arizona", "Cactus Wren", "Saguaro Cactus Blossom"},
            {"Arkansas", "Mockingbird", "Apple Blossom"},
            {"California", "California Valley Quail", "Golden Poppy"},
            {"Colorado", "Lark Bunting", "Rocky Mountain Columbine"},
            {"Connecticut", "Robin", "Mountain Laurel"},
            {"Delaware", "Blue Hen Chicken", "Peach Blossom"},
            {"Florida", "Mockingbird", "Orange Blossom"},
            {"Georgia", "Brown Thrasher", "Cherokee Rose"},
            {"Hawaii", "Nene", "Hawaiian Hibiscus"},
            {"Idaho", "Mountain Bluebird", "Syringa, mock orange"},
            {"Illinois", "Cardinal", "Violet"},
            {"Indiana", "Cardinal", "Peony"},
            {"Iowa", "Eastern Goldfinch", "Wild Praire Rose"},
            {"Kansas", "Western Meadowlark", "Sunflower"},
            {"Kentucky", "Cardinal", "Goldenrod"},           
            {"Louisiana", "Eastern Brown Pelican", "Magnolia"},
            {"Maine", "Chickadee", "Pine Cone and Tassel"},
            {"Maryland", "Baltimore Oriole", "Black-Eyed Susan"},
            {"Massachusetts", "Chickadee", "Mayflower"},
            {"Michigan", "Robin", "Apple Blossom"},
            {"Minnesota", "Common Loon", "Pink and White Lady's Slippper"},
            {"Mississippi", "Mockingbird", "Magnolia"},
            {"Missouri", "Bluebird", "Hawthorn"},
            {"Montana", "Western Meadowlark", "Bitterroot"},
            {"Nebraska", "Western Meadowlark", "Goldenrod"},
            {"Nevada", "Mountain Bluebird", "Sagebrush"},
            {"New Hampshire", "Purple Finch", "Purple Lilac"},
            {"New Jersey", "Eastern Goldfinch", "Violet"},
            {"New Mexico", "Roadrunner", "Yucca Flower"},
            {"New York", "Bluebird", "Rose"},
            {"North Carolina", "Cardinal", "Flowering Dogwood"},
            {"North Dakota", "Western Meadowlark", "Wild Praire Rose"},
            {"Ohio", "Cardinal", "Scarlet Carnation"},
            {"Oklahoma","Scissor-tailed Flycatcher","Oklahoma Rose"},            
            {"Oregon", "Western Meadowlark", "Oregon Grape"},
            {"Pennsylvania", "Ruffed Grouse", "Mountain Laurel"},
            {"Rhode Island", "Rhode Island Red", "Violet"},           
            {"South Carolina", "Great Carolina Wren", "Yellow Jessamine"},
            {"South Dakota", "Ring-necked Pheasant", "Pasque Flower"},
            {"Tennessee", "Mockingbird", "Purple Passionflower"},
            {"Texas", "Mockingbird", "Bluebonnet Sp."},
            {"Utah", "Common American Gull", "Sego Lily"},
            {"Vermont", "Hermit Thrush", "Red Clover"},
            {"Virginia","Cardinal"," American Dogwood"},
            {"Washington", "Willow Goldfinch", "Coast Rhododendrum"},
            {"West Virginia", "Cardinal", "Rhododendron"},
            {"Wisconsin", "Robin", "Wood Violet"},
            {"Wyoming", "Western Meadowlark", "Indian Paintbrush"}
        };

        for (String[] info : stateInformation) {
            states.put(info[0].toLowerCase(), new StateInfo(info[0], info[1], info[2]));
        }
    }

    public StateInfo(String name, String bird, String flower) {
        this.name = name;
        this.bird = bird;
        this.flower = flower;
    }

    public static StateInfo getInfo(String stateName) {
        return states.get(stateName.toLowerCase());
    }

    public static void main(String[] args) {

        Scanner userInput = new Scanner(System.in);

        while (true) {
            System.out.println("Enter a State or None to exit:");
            String stateName = userInput.next();

            if (stateName.equalsIgnoreCase("None")) {
                break;
            } else {
                StateInfo stateInfo = getInfo(stateName);
                if (stateInfo != null) {
                    System.out.println("Bird: " + stateInfo.bird);
                    System.out.println("Flower: " + stateInfo.flower);
                } else {
                    System.out.println("Invalid State Entered");
                }
            }
        }
    }
}
\$\endgroup\$
4
  • 1
    \$\begingroup\$ n is a constant here, so O(n) = O(1). Did you do a benchmark to compare the performance of the array against that of the HashMap? If not, then "you can do much better by building a HashMap" is misleading. In fact, it could very well be the case that the locality of a contiguous array would prove more important for performance than the complexity boost that using a HashMap yields in the general case. \$\endgroup\$
    – Clément
    Jul 4, 2015 at 19:01
  • 1
    \$\begingroup\$ Of course, it's good to know about hashmaps, and the code will be a lot more maintainable once the getInfo function is gotten rid of. Replacing it by stream functions wouldn't be bad either IMHO; see stackoverflow.com/questions/23696317/… for example. \$\endgroup\$
    – Clément
    Jul 4, 2015 at 19:04
  • \$\begingroup\$ You're right about O(n) vs O(1), updated that part. While it's true that using a Map in this particular example won't make a noticeable difference, it's a matter of principle to use something better than search by iteration. Using a Map would have naturally preempted that silly position variable \$\endgroup\$
    – janos
    Jul 4, 2015 at 19:13
  • \$\begingroup\$ Agreed, but in this case I really think that using a Map is a good practice, with no performance considerations; lookups will be just as instantaneous without a Map. \$\endgroup\$
    – Clément
    Jul 4, 2015 at 19:16
8
\$\begingroup\$

Just a small nitpick about this:

public static int getInfo(String stateInfo[][],String state)

It is uncommon to declare a 2-d array as String stateInfo[][], especially in a method header.

It is much more common to put the [] directly after the type, like this:

public static int getInfo(String[][] stateInfo, String state)
\$\endgroup\$
7
\$\begingroup\$

Your program does not work for states with names that contain more than one word.

getInfo() returns an index to the information rather than the information itself. A better name would be findState().

The search loop is clumsy. It would be easier if you immediately return when a match is found.

public static int findState(String[][] stateInformation; String state) {
    for (int i = 0; i < stateInformation.length; i++) {
        if (stateInformation[i][0].equalsIgnoreCase(state)) {
            return i;
        }
    }
    return -1;
}
\$\endgroup\$
7
\$\begingroup\$

Disclamer: Not a Java expert.

System.exit

First off, why are you using System.exit(0) to exit the loop? Wouldn't it be easier to just use break?

Spacing issues

Secondly, you have some spacing issues. For example,

for (int index=0; index<stateInfo.length && !found; index++)

would become

for (int index = 0; index < stateInfo.length && !found; index++)

and this:

getInfo(String stateInfo[][],String state)

would become

getInfo(String stateInfo[][], String state)

and position=index; would become position = index;

Using a Map

Finally, even though the challenge says to use a multi-dimensional array, I'd still recommend using a Map rather than an array. The array is rather clunky anyways. Here's how you'd do that.

Map<String, String[]> stateInfo = new HashMap<String, String[]>();

Then to add an element, simply do this:

stateInfo.put("Statename", { ... });

And finally, to get an item from the Map, just do this:

stateInfo.get("Statename");
\$\endgroup\$
1
  • \$\begingroup\$ I like that this answer advocates the Map for readability and convenience, not for supposed performance benefits :) \$\endgroup\$
    – Clément
    Jul 4, 2015 at 19:05
6
\$\begingroup\$

Although the hint said to store information in a multi-dimensional array, it would be much better to store the information in an enum type. Basically an enum is a constant object. You will not be creating new states or changing the information about the states during runtime, so it makes sense to use something that will keep them constant.

Here is what your code my look like as an enum:

public enum StateInformation {
    ALABAMA("Alabama", "Yellowhammer", "Camelia"),
    ALASKA("Alaska", "Willow Ptarmigan", "Forget-Me-Not"),
    //[...]
    WYOMING("Wyoming", "Western Meadowlark", "Indian Paintbrush");

    private String name;
    private String bird;
    private String flower;

    StateInformation(String name, String bird, String flower){
        this.name = name;
        this.bird = bird;
        this.flower = flower;
    }

    public String getName(){
        return this.name;
    }

    public String getBird(){
        return this.bird;
    }

    public String getFlower(){
        return this.flower;
    }
}

There are a number of advantages of an enum:

  • Say you wanted to add the year (as an Integer) that the state was founded. With a 2D array, you would have to make it an Object[][], thus losing type safety. With an enum, you can still enforce type safety because fields can be of different values.
  • What would happen if your 2D array was missing a value or had an extra value. You wouldn't know at compile time and would run into unexpected behavior (or an exception) at runtime. An enum can spot those errors during compile time.
  • Code is easier to read and maintain. StateInformation.WYOMING.getStateBird() is much easier than stateInformation[50][1]. Also since we aren't relying on numbers, we could add a state or a field and nothing would break.
  • You can pass a StateInformation object as a parameter
  • Since it is a class, you can control where it is accessible. If it is public, you can access it in any class that it is imported in (just like how you can access Math.PI anywhere you import it)
  • Because enums are objects, you can sort them using a Comparator.

Also by making the state and the information an enum type, you can add methods to each StateInformation enum.

public boolean stateBirdIsMultipleWords(){
    return this.bird.contains(" ");
}

Enums are an essential part of object-oriented programming and something that would make this code much easier to read and more maintainable.

\$\endgroup\$
1
  • \$\begingroup\$ Nice answer about the use of enums, I'll do without the name field though as that can be retrieved from the enum name. The lookup can be done via StateInformation.valueOf(input.toUpperCase()) (need to handle spaces carefully though) \$\endgroup\$
    – h.j.k.
    Jul 5, 2015 at 6:43
2
\$\begingroup\$

return from main to exit a program

Other contributors have told you that you should not user System.exit(0). But break is not a general solution. There may be some code after the main loop of the program that wouldn't be executed if you exited with a System.exit. Or you may want to exit outside of a loop, e.g. because of invalid commandline args. Whenever you think you need System.exit try to use a return instead. main method is not magic, and you can return from it as any other method.

Handle the exceptional case first

Handling the exceptional case first allows you to forget about it promptly and concentrate on the interesting case instead. See for example this question.

This means; you needn't worry about user entering None, provided you return early.

if(stateName.equalsIgnoreCase("None")) return; // no else needed

The equivalent of returning early from a method in a loop is continue statement:

if (position == -1) {
    System.out.println("Invalid State Entered");
    continue;
}

With these to changes applied nesting and branching in the main method is reduced, and it reads from top down easily:

public static void main(String[] args) {

    Scanner userInput = new Scanner(System.in);

    while (true) {
        System.out.println("Enter a State or None to exit:");
        String stateName = userInput.next();

        if (stateName.equalsIgnoreCase("None")) {
            return;
        }

        int position = getInfo(stateInformation, stateName);

        if (position != -1) {
            System.out.println("Invalid State Entered");
            continue;
        }

        System.out.println("Bird: " + stateInformation[position][1]);
        System.out.println("Flower: " + stateInformation[position][2]);
    }
}
\$\endgroup\$
0
\$\begingroup\$

Creating loops like while (true) is not advisable as per the best coding practices. You may create a Boolean flag and use it in your method as:

boolean flag = true;

// ...

while (flag) {
    System.out.println("Enter a State or None to exit:");
    String stateName = userInput.next();

    if (stateName.equalsIgnoreCase("None")) {
        flag = false;
    }

    // ...

}

Though this is a relatively simple program, but in more complex situations, a variable gives more control and reliability under such situations.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.