The second assignment is mostly about arrays, notably our own array specifications and implementations, not just the built-in Java arrays. First, we will re-visit the Unique
program but with our own Array
implementation. Then you will be programming a different implementation of our Array
interface.
Note: This assignment we will require your Java source to be checkstyle compliant with the provided configuration file! You will lose 1 point for each type of checkstyle violation; excessive instances of the same type of violation might be docked extra.
This assignment has a slightly more advanced package setup. Now we have two subpackages. This setup will provide you with files exactly how they exist in the autograder.
Files you will be editing are marked with an asterisk (*).
hw2-student.zip
--
exceptions/
IndexException.java
LengthException.java
hw2/
Array.java
ListArray.java
PolyArray.java *
SimpleArray.java
SparseArray.java *
Unique.java *
These provided files should compile as is. You can compile everything by running $ javac -Xlint:all hw2/*.java exceptions/*.java
, or using the generic compile script from the Java Command Line notes. You should only need to compile exceptions/*.java
one time.
You wrote a small Java program called Unique
for Assignment 1 which got it’s input in the form of command line arguments and printed each unique integer it received back out once, eliminating duplicates in the process.
For this problem, you will implement a new version of Unique
. Note that this will be the hw2
package version of Unique
so you can distinguish between the two even though they have the same name (running hw2.Unique
is different from running hw1.Unique
). This new version will have two major changes:
First, you are no longer allowed to use Java arrays (nor any other advanced data structure), but you can use our Array
interface and our SimpleArray
implementation provided in the zip.
Second, you’re going to modify the program to read the integers from standard input instead of processing the command line.
So the resulting Unique
program is invoked as
$ java hw2.Unique
with no further arguments (or rather, the program ignores any arguments the user passes to it). The program then waits for input from the user, who could for example type
1 9 2
3
1 4
9 5 3
6 0
and then hit the end-of-file (EOF) key - that’s CTRL-D on Unix and CTRL-Z on Windows. At this point the program should output the unique numbers that were present in the input just like before:
1
9
2
3
4
5
6
0
Note, like before the output numbers dont need to be sorted, and non-integer values should be treated as before and cause an exception. The files Array.java
and SimpleArray.java
are in the hw2
package.
Please use those official files and do not edit them - the autograder will replace those files with the unedited version anyways.
In your README, you should address the following: 1. Discuss your approach to doubling the array.
Reading numbers from standard input can be accomplished using a java.util.Scanner
object that has been wrapped around System.in
which is Java’s name for the standard input stream.
Make sure you hit return one last time at the end of your input and only then signal end-of-file with the appropriate key-combination for your operating system. (This restriction doesn’t apply when you use I/O redirection to give input to the program, a highly recommended practice for testing).
End-of-file in a unix/linux environment is usually ctrl-d, but it’s ctrl-z in a windows environment. In Intellij, take a look here for more information.
You will have to process an unbounded number of inputs, which requires that you keep track of how “full” the array is. When nothing fits into the array anymore, you’ll have to “grow” it somehow. The best approach is to double the size of the array when you are out of space.
Do not try to change everything at once, there are too many “moving parts” to get things right that way. Instead, choose one thing to change,for example just the way input is given to the program, finish that, test it, and only then move on to the next thing. Remember: Baby steps!
Ideally, the type of your object holding the data should be the interface class, an Array<Integer>
. This way you can swap out the implementation just by changing which array you instantiate. Note the type of an Array
must be an object and cannot be a primitive type, Array<int>
is invalid in Java.
A sparse array is an array in which relatively few positions have values that differ from the initial value set when the array was created. For sparse arrays, it is wasteful to store the value of all positions explicitly since most of them never change and take the default value of the array. Instead, we want to store positions that have actually been changed.
For this problem, write a class SparseArray
that implements the Array
interface we developed in lecture (the same interface you used for Part A above). Do not modify the Array
interface in any way! Instead of using a plain Java array like we did for SimpleArray
, your SparseArray
should use a linked list of Node
objects to store values, similar to the ListArray
in the hw2
package. However, your nodes no longer store just the data at a certain position, they also store the position itself!
Here’s a rough outline of how your implementation could work:
Start with an empty list (instead of the complete list we built in the constructor of ListArray
).
For put
, check if the relevant position has been modified before (meaning a Node
object exists for that position); if not, add a Node
to the list for the position and its new value; otherwise update the correct Node
to the new value.
For get
, check if the relevant position has been modified before; if not, return the default value; otherwise, return the value found in the relevant Node
object.
Important: Your Node
class must be nested inside your SparseArray
class with private
visibility! Clients should not be able to “touch” Node
objects in any way!
Make sure your Javadoc comments include advice for clients trying to decide between the regular SimpleArray
implementation and your new sparse implementation.)
As part of Assignment 1, we gave you a program called PolyCount
that could be used to test the basic operation of a number of different Counter
implementations.
For this assignment, we’re giving you a similar program called PolyArray
that can be used to test array implementations (some of the details are a bit more complex for technical reasons, but you don’t need to understand those for now).
However, PolyArray
is far from complete regarding test cases:
the code in testNewLength
only covers 1 axiom of the specification
the code in testNewLengthWrong
only covers the 1 precondition of the specification
You need to modify PolyArray.java
to add more test cases so that the entire implementation is tested.
Of course, you should also use PolyArray
to test your new SparseArray
code. Once you’re done with PolyArray
all three implementations should be fully tested when the program is run.
Make sure in your final deliverable you pass your own tests from this file as we will be checking this in the autograder.
In your README, you should address the following: 1. When would a SparseArray
be useful and why? 2. Why is it that SparseArray
must implement iterator()
?
Your iterator for SparseArray
doesn’t have to be particularly efficient; indeed, it’s rather tricky to make it work fast; don’t try. (Or rather: Only try if you’re bored!)
Array
to look like. You don’t just iterate over your own list (i.e. copying the iterator from ListArray
)Think about this: Someone creates a SparseArray<Integer>
with an initial value of 1. Then they put a few numbers different from 1 into the array. For example, the value of slot 4 might be 18. What should happen for put(4, 1)
?
Testing a precondition means testing that the correct exception is thrown when a bad parameter is provided.
Note that the PolyArray
we gave you doesn’t test iterators at all; it probably should since all array implementations (the ones we gave you and the one you wrote yourself) support iterators…
SimpleArray
has one inevitable unchecked cast warning. When you compile, this one warning is fine, but your own code should not add any additional warnings.
The files you have // TODO
items in are listed explicitly below:
Unique.java
SparseArray.java
PolyArray.java
You need to submit all of these files to the autograder along with a README. You can upload them individually or in a zip file. If you upload them in a zip file make sure they are all at the top level, you cannot have any extra directories or else the autograder won’t be able to find them.
Make sure the code you hand in does not produce any extraneous debugging output. If you have commented out lines of code that no longer serve any purpose you should remove them.
You must hand in the source code and a README file. The README file can be plain text (README
with no extension), or markdown (README.md
). In your README be sure to answer the discussion questions posed in this description. You should discuss your solution as a whole and let the staff know anything important. If you are going to be using late days on an assignment, we ask that you note it in your README.
If you want to learn markdown formatting, here is a good starting point.
Once you are ready to submit your files, go to the assignment 2 page for Gradescope and click submit. Note that you can resubmit any time up until the deadline. Only your most recent submission will be graded. Please refer to course policies as far as policies regarding late days and penalties.
After you submit, the autograder will run and you will get feedback on your functionality and how you performed on our test cases. Some test cases are “hidden” from you so you won’t actually know your final score on the test cases until after grades are released. We also include your checkstyle score as a test case.
If you see the “Autograder Failed to Execute” message, then either your submission did not compile at all or there was a packaging error. Please see the Gradescope Submission Notes for help debugging why your submission is not working.
You do not need to fully implement each file before you submit, but you’ll probably fail the test cases for the parts of the assignment you haven’t done yet. Also note that only the files with // TODO
items in them will be used. You cannot modify any of the provided interface files as the autograder will overwrite any changes you made with the original provided file.
For reference, here is a short explanation of the grading criteria; some of the criteria don’t apply to all problems, and not all of the criteria are used on all assignments.
Packaging refers to the proper organization of the stuff you hand in, following both the guidelines for Deliverables above as well as the general submission instructions for assignments.
Style refers to Java programming style, including things like consistent indentation, appropriate identifier names, useful comments, suitable javadoc
documentation, etc. Many aspects of this are enforced automatically by Checkstyle when run with the provided configuration file.
public
, protected
, and private
appropriately, etc.). Simple, clean, readable code is what you should be aiming for.Testing refers to proper unit tests for all of the data structure classes you developed for this assignment, using the JUnit 4 framework as introduced in lecture. Make sure you test all parts of the implementation that you can think of and all exception conditions that are relevant.
Performance refers to how fast/with how little memory your program can produce the required results compared to other submissions.
Functionality refers to your programs being able to do what they should according to the specification given above; if the specification is ambiguous and you had to make a certain choice, defend that choice in your README
file.
If your submission does not compile, you will not receive any of the autograded-points for that assignment. It is always better to submit code that at least compiles. You will get freebie points just for compiling.
If your programs have unnecessary warnings when using javac -Xlint:all
you will be penalized 10% functionality per failed part. (You are also unable to use the @SuppressWarnings
annotation - we use it just to filter our accepted warnings from yours.)
If your programs fail because of an unexpected exception, you will be penalized 10% functionality per failed part. (You are not allowed to just wrap your whole program in to a universal try-catch.)