Due Wednesday, April 17 at 11:59pm.
Submit by pushing your code to GitLab. If you intend to submit late, fill out this late submission form when you submit.
In this half of the assignment, you will implement the data structures you will be using for the rest of the quarter in later projects. For this reason, it's very important in this part of the project to make sure you work out all of the potential bugs you might have in your code—oversights in this part may end up causing bugs for the entire rest of the course.
You will be modifying the following files:
src/main/java/datastructures/concrete/DoubleLinkedList.java
src/test/java/datastructures/TestDoubleLinkedListDelete.java
src/main/java/datastructures/concrete/dictionaries/ArrayDictionary.java
Additionally, here are a few more files that you might want to review while completing the assignment (note that this is just a starting point, not necessarily an exhaustive list):
src/test/java/datastructures/TestDoubleLinkedList.java
src/test/java/datastructures/BaseTestDoubleLinkedList.java
src/main/java/datastructures/interfaces/IList.java
src/test/java/datastructures/dictionaries/TestArrayDictionary.java
src/test/java/datastructures/dictionaries/BaseTestDictionary.java
src/main/java/datastructures/interfaces/IDictionary.java
src/main/java/analysis/experiments/*
WARNING:
For all assignments, you should modify only the files we explicitly list at the top of the
assignment page as needing modifications. All changes in other files will be
ignored during grading. If you accidentally change one of these files, your
code may not compile when we're grading, in which case we may not grade your code, and
you'll get a zero on the assignment. If you need to temporarily change one of
these files for debugging, make sure you revert your changes afterwards.
Also, absolutely DO NOT change the gitlab-ci.yaml
config file. This file
affects what the GitLab runners run; you have no reason to change this, and if you do, you may
lose the ability to get feedback from the runners. Also don't change the
build.gradle
file, since your IDE needs that check project dependencies.
Note that the most important information for the programming assignments (the expected input and output, and any special cases) tend to be found in the files themselves. This page serves as an general guide to the assignment; the details tend to be found more often within the files themselves.
We recommend reading through this entire assignment page before you begin writing code. We also have a video overview for this part with some additional tips for after you read the assignment, but before you start coding.
DoubleLinkedList
Task: Complete the DoubleLinkedList
class.
A doubly-linked list is a similar to the singly-linked lists you studied in CSE 143 except in two crucial ways: your nodes now have pointers to both the previous and next nodes, and your linked list class has now have pointers to both the front and back of your sequence of list node objects.
Visually, the singly linked lists you studied in CSE 143 look like this:
Doubly-linked lists containing the same data will look like this:
Your implementation should:
IList
interface. This means you will be using this file extensively as a template
for what your code will do.Warning: correctly implementing a doubly-linked list will require you to pay careful attention to edge cases. Some tips and suggestions:
Write pseudocode for your methods before writing code. Avoid immediately thinking in terms of list node manipulation – instead, come up with a high-level plan and write helper methods that abstract your node manipulations. Then, flesh out how each helper method will work.
Or to put it another way, figure out how to refactor your code before you start writing it. Your code will be significantly less buggy that way.
int
, double
, etc). This will come up
in two ways: one, you'll need to remember that changing an object might change
the reference in another place (Check the Week 1 QuickCheck for an example),
and two, you'll need to remember to use ==
to compare equality for
primitives and null
s, and use .equals()
for object
comparisons. Tip: you may use java.util.Objects
to handle your
equality checks with possibly-null values instead of juggling the ==
and .equals()
yourself. Here's documentation for the relevant method.Task: Add tests for the delete
method to
TestDoubleLinkedListDelete
.
In part 1b, you will practice writing some unit tests using JUnit.
Start by skimming through TestDoubleLinkedList.java
and
familiarize yourself with the tests we have given you. Since this is the
first assignment, we've given you most of the tests you need, except for
a few. Can you see what tests are missing?
There are no tests for the DoubleLinkedList.delete(...)
method! Your job here is to write tests for this method.
You are responsible for writing tests that ensure your delete method is fully working. The ability to create a comprehensive set of tests for a given piece of code is a foundational skill in programming. To do this you'll want to think about all the different ways your code could be broken. Often it is helpful to think of these possible bugs in categories:
To help you out this first time, here are some cases you will need to write tests for specific to your delete method. Turn each of the following cases into its own JUnit test method.
Whenever you want to check the state of the list, you'll need to call its methods and use one of the JUnit assert methods to check whether our expected value matches the actual value the list returns. When implemented as intended, each of the cases below (except for the invalid input case) should have at least one assert statement. It's also important to think about how each test case affects different aspects of the state of the list to ensure that your tests check all the necessary methods of the list.
Note: this is not a comprehensive list of cases, so you should add more tests as you think of them.
[redacted]
A few additional notes:
Please keep all tests within TestDoubleLinkedListDelete
short.
Every test you add to this file should have a timeout of a second or less.
(You're encouraged to add as many tests as you want, however.)
You may find the following documents from the resources page to be particularly useful:
Task: Complete the ArrayDictionary
class.
Your ArrayDictionary
class will internally keep track of
its key-value pairs by using an array containing Pair
objects.
Dictionary<String, Integer> foo = new ArrayDictionary<>();
foo.put("a", 11);
foo.put("b", 22);
foo.put("c", 33);
Your internal array should look like the following:
Now, suppose the user inserted a few more keys:
foo.put("d", 44);
foo.remove("b");
foo.put("a", 55);
Your internal array should now look like the below. Notice that we've updated the old key-value pair for "a" to store the new value. We've also removed the key-value pair for "b".
This means you will need to scan through the internal array when retrieving, inserting, or deleting elements. If your array is full and the user inserts a new key, create a new array that is double the size of the old one and copy over the old elements. Minor design decisions, like the initial capacity of the array, are left up to you; choose something that reasonable and adjust if it seems necessary.
Once completed, the design and logic of your
ArrayDictionary
should feel very similar to the
ArrayIntList
objects you previously studied in CSE 143.
There is one general optimization we will have you implement.
Because the values in the dictionary are inherently unordered, we
can use this to our benefit in the remove
method.
Instead of shifting over all the elements as you would normally need
to do to remove from an array, you should instead just replace the
value stored at the index containing the element to be removed to be
the last pair currently in the ArrayDictionary. Here is an example of
what your internal representation may look like before, during, and
after a single call to dict.remove("a")
.
Task: Complete a writeup containing answers to the following questions.
Your writeup will be graded partially on whether or not you produced a reasonable answer, and partially based on the clarity of your explanations and analysis. You and your partner will work together on this writeup.
Note: your completed writeup MUST be in PDF format. You will not submit this writeup via Canvas—instead, submit it to Gradescope here. Log into Gradescope with your @uw.edu email address. When you submit, make sure to mark which pages correspond to the questions we ask, and after submission the partner who submitted the assignment must add the other partner. That is, please only turn in one submission per group and make sure both group members are assigned to that submission. A video showing how to do this can be found here.
You will run a variety of experiments and report back on the resulting behavior. Since this is the first assignment, we have written the experiments for you so all you need to do is run them. You will be asked to write your own experiments in future projects, so be sure to take some time to read the provided code and understand what's going on.
You can find the experiments in the analysis.experiments
package.
For each of the four experiments, answer the following:
Briefly, in one or two sentences, summarize what this experiment is testing. (Note: some lines include comments about what they do, but not all. Treat this as an exercise in reverse-engineering unknown code.) When reading these experiments, you can generally overlook "main" (which contains a lot of Java-specific shorthand) and pay closer attention to the "test" methods that you'll find below. A comment will describe the calls that main is making.
Predict what you think the outcome of running the experiment will be, in a few sentences.
There is no right or wrong answer here, as long as you thoughtfully explain the reasoning behind your hypothesis.
Run the experiment code. Each Experiment*.java
file should
generate a CSV file in the experimentdata
folder
(it is possible that the 4th experiment will produce a warning; this is
can safely be ignored, as long as the CSV file in generated as described).
We recommend using plot.ly to generate a plot
from your CSV, but if you prefer you can also use Microsoft Excel, Google Sheets, or any other
analysis tool of your choice. Generate a plot of the data, and include this
plot as a image inside your writeup.
If the CSV file contains multiple result columns, plot them all
in the same chart.
If you're using plot.ly, here are some basic instructions:
You will be graded based on whether you produce a reasonable plot. In particular, be sure to give your plot a title, label your axes (with units), include a legend if appropriate, and relabel the columns to something more descriptive than "TestResult".
How do your results compare with your hypothesis? Why do you think the results are what they were?
You should have seen some interesting patterns in the plots you made! In this part of the writeup, we want to hear about why you think these things happened in the lens of what we're doing in lecture.
The answers to these questions don't need to be very long; a couple sentences for each is enough.
get()
is much slower
than the iterator()
to traverse your DoubleLinkedList
—why
is that? Similarly, why are the runtimes for the iterator()
and the for-each loop so similar?get()
works very quickly for two areas of your
DoubleLinkedList
: the front and back. It should get slower as the indices you get are closer to the middle. Why is that?
State the complexity class (choose from: constant, linear, quadratic, logarithmic, exponential, etc.)
of the runtime of getting from the front, getting from the back, and getting from the middle to
support your answer.DoubleLinkedList
increases by a constant amount as the input list size increases. This is in contrast to
ArrayDictionary
, which increases in memory usage by about the same amount
on average, but sometimes has spikes in memory usage. What is the cause of the
spikes in the data for ArrayDictionary
, and why doesn't
DoubleLinkedList
spike in memory usage in the same way?The following deliverables are due on Wednesday, April 17 at 11:59pm.
Submit by pushing your code to GitLab. If you intend to submit late, fill out this late submission form when you submit.
Before submitting, be sure to double-check that:
TestDoubleLinkedListDelete
,
DoubleLinkedList
, and ArrayDictionary
classes, which all
behave as expected.