To understand how to implement a List variant using links (specifically, a sorted linked-list), and to see how testing up front can help you when writing code.
This is a homework assignment that you'll be handing in on Moodle.
You're welcome to either complete this homework with input from one or more members of your collaborative learning group or to complete it on your own. You and the other members of your collaborative learning group should each write your own code, even if you're helping one another, but you may share your screens/use multiplayer mode to look at one another's code, make suggestions, and compare ideas. Take a look at the Collaboration Policies document on Moodle if you have any questions about what's permitted.
The code that you write for this assignment will build on your CountryDisplayer (HW3) code and use the SortedList ADT, a variation of the List ADT. The files you will need are:
You should not make any changes to SortedList, BarChart, or the jar file. There is a Javadoc for SortedList here.
You'll get all of these files on repl.it. Go to our class, then the Projects section, and choose HW05: Country Displayer Redux.
Once you've gotten the project, you should also upload your Country.java and CountryDisplayer.java files. You can do this by double clicking on the zip file that you previously uploaded to Moodle for HW03, and then dragging these two files onto the "Files" portion of the repl.it window.
public int compare(Country country1, Country country2)
Create a new file named CountryComparator.java that implements this class. It should include a constructor that takes two arguments, in the following order: (1) a string specifying the indicator to sort on (one of: CO2Emissions, AccessToElectricity, RenewableEnergy, ProtectedAreas, PopulationGrowth, PopulationTotal, or UrbanPopulationGrowth), (2) a boolean indicating whether the sorting should be from greatest to least or least to greatest (if true, the sorting should be from greatest to least). The compare method for the instance should then order countries based on the values passed in to the constructor. We'll break ties based on the country name, with country names that are alphabetically earlier appearing before those that are alphabetically later. (So, the compare method will only return 0 if the indicator values are the same and the country names are the same.)
For example, if we had Country instances akhaten (95% access to electricity), trenzalore (65% access to electricity), and gallifrey (95% access to electricity), each of which has the same country name as the variable names, here's some snippets of code showing what we expect:
Comparator<Country> countryComparator = new CountryComparator("AccessToElectricity", false);
//Value for akhatenVersusTrenzalore should be positive (trenzalore has less access)
int akhatenVersusTrenzalore = countryComparator.compare(akhaten, trenzalore);
//Value for akhatenVersusGallifrey should be negative
//(they have the same access, and akhaten is alphabetically first)
int akhatenVersusGallifrey = countryComparator.compare(akhaten, gallifrey);
javac Country.java CountryComparator.java
public Country(String[] countryData);
Country Name CO2 emissions (metric tons per capita) Access to electricity (% of population) Renewable energy consumption (% of total final energy consumption) Terrestrial protected areas (% of total land area) Population growth (annual %) Population (total) Urban population growth (annual %)
Before you continue, you'll implement one more thing: an equals method in Country, with the signature : equals(Country otherCountry). This method should return true if two Country instances have values that are the same for all indicators and have the same name. Otherwise, it should return false. Let's define "the same for all indicators" as for each indicator value, the value for the first country is within 0.001 of the value for the second country. So, the two countries could have values of 99.999 and 99.998 for access to electricity and still potentially be equal.
Again, test and make sure your implementation works before you continue. You'll likely want to put your tests for this part in the main method for Country.
You will write a class SortedLinkedList that storesCountry instances and implements all of the methods in the SortedList interface (except maybe iterator(); see below). Your list will take in a comparator upon construction and will always be sorted such that add(Country newItem) places the item into its appropriate place in the list to maintain sorted order. It will also have a resort method that allows the list to be resorted according to a different comparator. Take a moment and look at the SortedList interface. It's very similar to List, except for the way that add works, the resort method, and the fact that you can't replace an item or add an item at a specific position.
You will write a class SortedLinkedList that implements all of the methods in the SortedList interface. Your SortedLinkedList only needs to store Country instances, so you'll indicate you are implementing this interface with the generic always being Country:
public class SortedLinkedList implements SortedList<Country> {
//Your implementation here!
}
Your implementation must use a linked structure to store entries in the list (not an array structure - you'll get more practice with arrays in future assignments). Your class should not use any built in Java List/LinkedList classes - you're writing your own implementation of a linked list!
Start off by copying all of the method signatures from SortedList into SortedLinkedList. For the void methods, you don't need to put any code into the method bodies yet; for the methods that return something, just have them return some default value (e.g., false or -1 or null, depending on the type you need to return). Update (added 25 April): Additionally, add a constructor that takes in a Comparator<Country> to tell the list how to sort. You don't need to make the constructor do anything yet.
You should now be able to compile your code without any errors:
javac -classpath .:jfreechart-1.5.0.jar:/run_dir/junit-4.12.jar:/run_dir/hamcrest-core-1.3.jar:/run_dir/json-simple-1.1.1.jar *.java
Begin your implementation by putting in your instance variables and making the constructor set these appropriately. Then, you'll go through your methods one by one and implement them; you should test as you go - see the section below for details. I encourage you to start with size() and add(Country newItem), and then branch out from there. When you've finished implementing SortedLinkedList, you should be able to run all of my tests in TestSortedListJUnit and see that they all pass.
Iterator method: Fully implementing the iterator() method is optional. If you choose not to fully implement it, then calling this method should throw an UnsupportedOperationException. If you choose to implement an iterator, see these notes to find out more about how to do it. If you go this route, you should be able to write an iterator where both next() and hasNext() are O(1).In addition to your list being a linked list, there's one additional constraint on your implementation: the resort method should not make any new objects, either directly or indirectly. That means it should new use the new keyword, and it shouldn't call any methods that use the new keyword either. Said another way, resort should either move nodes around or move items between nodes - it shouldn't make new nodes.
Finally, in the comment for each public method you implement, please indicate its worst case running time in big-O notation in terms of the length n of the list.
To make it easier for you to ensure that your list is correct, I've written some tests for you in TestSortedLinkedListJUnit.java. These tests will help you to understand whether your code is working as expected; you're also welcome to write additional tests if you like.
To compile the testing code, you'll type:
javac -classpath .:jfreechart-1.5.0.jar:/run_dir/junit-4.12.jar:/run_dir/hamcrest-core-1.3.jar:/run_dir/json-simple-1.1.1.jar *.java
Once you've created a SortedLinkedList class that has the appropriate method signatures and bodies that just return the right sort of thing, you should be able to compile without errors.
When you actually want to run the tests, you'll run:
java -classpath .:jfreechart-1.5.0.jar:/run_dir/junit-4.12.jar:/run_dir/hamcrest-core-1.3.jar:/run_dir/json-simple-1.1.1.jar Main
Note that I've been able to write tests for your code even though haven't actually implemented the code yet. Writing tests first helps you think about what should happen, and makes it easier to incrementally build your implementation rather than writing the whole thing. It's helpful to make some tests that are separate from one another, testing only a few methods at a time, so when one fails, you know which method in your implementation is likely responsible for that failure.
Take a look at my testing code in TestSortedLinkedListJUnit.java to see what sorts of things are being checked. It uses some methods you aren't familiar with yet, like assertEquals, assertFalse, and assertTrue: these methods will throw errors if the inputs are not equal/true/false, allowing you to detect when your code isn't working as expected (e.g., to throw an error when the isEmpty() method on a newly created list doesn't return true).
Now, try running my tests:
java -classpath .:jfreechart-1.5.0.jar:/run_dir/junit-4.12.jar:/run_dir/hamcrest-core-1.3.jar:/run_dir/json-simple-1.1.1.jar Main
Most or all of these should fail, giving you a whole sequence of lines about failures. That's okay - you haven't implemented anything yet!
You should run the tests frequently as you implement the methods in SortedLinkedList. This can alert you early to any issues in your implementation.
When a test passes, you'll see a line like "TestSortedListJUnit#addTest passed.". Eventually, you want to get all of these lines to say that they passed.
When a test fails, you'll see a message like:
TestSortedListJUnit#clearTest had failures: clearTest(TestSortedListJUnit): Size should be zero after clear expected:<0> but was:<5>
If you want more detail about exactly what went wrong, you can add a "--verbose" in your command line:
java -classpath .:jfreechart-1.5.0.jar:/run_dir/junit-4.12.jar:/run_dir/hamcrest-core-1.3.jar:/run_dir/json-simple-1.1.1.jar Main --verbose
TestSortedListJUnit#clearTest had failures:
clearTest(TestSortedListJUnit): Size should be zero after clear expected:<0> but was:<5>
java.lang.AssertionError: Size should be zero after clear expected:<0> but was:<5>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:834)
at org.junit.Assert.assertEquals(Assert.java:645)
at TestSortedListJUnit.clearTest(TestSortedListJUnit.java:194)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at Main.runTest(Main.java:44)
at Main.runTests(Main.java:112)
at Main.main(Main.java:143)
I start reading this from the top and look for the first line involving TestSortedListJUnit: at TestSortedListJUnit.clearTest(TestSortedListJUnit.java:194). That tells me that the specific test that failed is on line 194.
You're also more than welcome to write your own tests to help understand what's going on in your code if you like.
The final thing you'll do in this assignment is to revise CountryDisplayer to make sure that it's done in an object-oriented style and the user can add or remove countries interactively.
Your new version of CountryDisplayer (keep the same name as for HW03) should have a constructor that takes one argument: a string with the location of the file with the countries (e.g., CountryDataset.csv):
/** * Constructs a new CountryDisplayer containing the countries in the file at filePath. * If filePath is null, constructs an empty CountryDisplayer. * * @param filePath path to the file from which to load country data * @param indicator indicator to use for sorting the countries * @param isGreatestToLeast whether the countries should be sorted from greatest to least */ public CountryDisplayer(String filePath, String indicator, boolean isGreatestToLeast);The countries should be loaded and stored in your SortedLinkedList. If the string is null or there is not a file at the corresponding filePath, your method should not crash but should simply construct a CountryDisplayer with no countries. You should be able to use much of your existing code for this part. Update: This is a bit inconsistent since it says both 1 argument and 3 arguments; whichever version you'd like is just fine.
Your code must also have the following four methods:
/** * Prints a text version of the countries (listed in the same format as in HW4) */ public void displayCountriesAsText(); /** * Displays a graph with the top 10 countries (based on the sorting criteria) * and a second series showing the additional indicator. * @param secondaryIndicator indicator to show as the second series in the graph */ public void displayGraph(String secondaryIndicator); /** * Changes the criteria for sorting * @param indicator indicator to use for sorting the countries Valid values are: CO2Emissions, * TotalGreenhouseGasEmissions, AccessToElectricity, RenewableEnergy, ProtectedAreas, * PopulationGrowth, PopulationTotal, or UrbanPopulationGrowth * @param isGreatestToLeast whether the countries should be sorted from greatest to least */ public void changeSortingCriteria(String indicator, boolean isGreatestToLeast); /** * Adds the given country to the data. * @param country country to add */ public void addCountry(Country country);Check and make sure that your method signatures (the name of the method, the return type, and the arguments passed in) exactly match the signatures above. You must have the right signatures to get credit. To implement the two display methods you will likely only have to make small revisions to your existing code. Note that if you don't implement iterator above, you cannot use the for-each loop (colon syntax) to work through the list. This likely means that your code will by asymptotically slower than if you were using the iterator - make sure you understand why this is true.
To compile your code, run:
javac -classpath .:jfreechart-1.5.0.jar:/run_dir/junit-4.12.jar:/run_dir/hamcrest-core-1.3.jar:/run_dir/json-simple-1.1.1.jar *.java
Finally, you'll change the way main behaves to allow the user to interactively add and display countries. The user should be able to run CountryDisplayer either with:
java -classpath .:jfreechart-1.5.0.jar CountryDisplayer CountryDataset.csv
java -classpath .:jfreechart-1.5.0.jar CountryDisplayer
You'll turn your code in on Moodle under the HW05: Country Displayer Redux assignment.
First, create a Collaborations.txt file in repl.it (using the "Add file" button). In that file, indicate in what ways (if any) you collaborated with other people on this assignment. Did you and your homework group look at each other's code? Talk about strategies? These are fine things to do, and you should note them in the Collaborations.txt file. If you used any resources outside of our course materials, that is also something to note in Collaborations.txt. Take a moment to review the homework and collaborations policy on Moodle.
After making Collaborations.txt and finishing the assignment, click on the three vertical dots next to the add file and add folder icons on repl.it, and choose "Download as zip" from the menu. Upload that zip on Moodle.
Below, I've listed the classes and methods you need to implement for this homework, and as always, you are welcome to write any additional methods or classes you like:
| Class | Required constructors, methods or interface implementations | Required command line functionality |
|---|---|---|
| Country | In addition to what you have from the first Country displayer, this should have: public Country(String[] countryData) countryData should have the same fields in the same order as the input file public boolean equals(Country otherCountry) Returns true if the two countries have the same indicators (within .001 of one another) and the same name |
None, although I encourage you to test your equals method in main. |
| CountryComparator | Implements Comparator<Country> interface, which requires implementing a single method: compare(country1, country2). Value of the comparison is based on the arguments pass in to the constructor. public CountryComparator(String indicator, boolean greatestToLeast) Indicator should be one of CO2Emissions, TotalGreenhouseGasEmissions, AccessToElectricity, RenewableEnergy, ProtectedAreas, PopulationGrowth, PopulationTotal, or UrbanPopulationGrowth. If greatestToLeast is true, sorting should be from greatest to least. The compare method for the instance should then based its order on these parameters, and should break ties based on country name, with country names that are alphabetically earlier appearing before those that are alphabetically later. |
None, although I encourage you to test your comparator in main. |
| SortedLinkedList | Must implement the SortedLinkedList interface. The iterator() method may throw an UnsupportedOperationException (implementing it fully is optional). May not use any built in Java Lists, and must use a link-based (not array-based) implementation. Implementation of resort should not construct any new objects, nor should it call any methods that construct new objects. (i.e., it should never use the new keyword, nor should it call anything that uses this keyword). The comment for public method includes the worst case running time in terms of the length n of the list (in big-O notation). |
None, although if you want to test your list in main, you're welcome to do so. |
| CountryDisplayer | Must have the following methods: public CountryDisplayer(String filePath, String indicator, boolean isGreatestToLeast) public void displayCountriesAsText() public void displayGraph(String secondaryIndicator) public void changeSortingCriteria(String indicator, boolean isGreatestToLeast) public void addCountry(Country country) |
Should work with both of the following cases: (1) no command line arguments, and (2) one command line argument, representing a file name with country data. Allows user to interact with the program via the following text commands "set sorting criteria", "add country", "display text", "display graph", and "exit". |
In addition to verifying that you have all of the constructors, methods, and classes above, here is a partial description of the things that we'll be looking for when evaluating your work (in addition to other style considerations discussed on Moodle):