600.226: Data Structures (Fall 2016)

Assignment 5: Sentinel Lists


The fifth assignment is mostly about implementing the List interface. Instead of the NodeList implementation we covered in class, you’ll write a SentinelList implementation as discussed below. Using these “sentinels” you’ll be able to write much simpler, much more concise code for almost all List operations.

I was going to include an application as well, but seeing as we have the midterm coming up next week, I wanted to make sure that this is a shorter assignment instead. (Rest assured that the next assignment will be longer again!)

Problem 1: Sentinel List Implementation (90%)

Your first task for this assignment is to write an implementation of the List interface called SentinelList. Just like for the NodeList implementation we covered in lecture, this requires that you deal with the Position and Iterator and Iterable interfaces as well.


There’s good news and bad news: The good news is that we’ll give you a working NodeList class to start from. However, the bad news is also that we’ll give you that working NodeList class to start from! Sounds confusing? Here’s what we mean:

It’s presumably “good” that we start you with code that “works” already, that way you should be able to make a few changes, test, make a few more changes, test again, etc. You definitely want to follow the “baby steps” philosophy again as much as you possibly can.

But it’s also “bad” that the code we give you is not particularly well-organized. The whole point of using “sentinel nodes” (see below) is to make the code much simpler and much more concise and if you stick too closely to what we give you, you can’t actually achieve that. You really must rethink the data structure from the ground up to do well!


The NodeList follows the basic pattern for linked lists we’ve used from the beginning of the course:

The result of this organization is that our code for modifying the list has to constantly check whether this, that, or the other thing is null and react accordingly. That means the code is full of conditionals where we do one thing for a null reference and another thing for a non-null reference. In other words, the code is horrible.

The idea for SentinelList is to change all that, to remove (almost) all the case distinctions around null references. To do that, we start from a simple premise, however that premise does take some getting used to:

Let this sink in for a little while, then draw some examples, starting with the empty list. As you’ll see, every insertion operation will now happen between two nodes, and there’s no longer any need to deal with null references. That’s where the big simplification comes from!

Note that if you don’t actually sit down and try this out with a piece of paper (or a whiteboard) you’ll never get it. We’re not kidding, Peter has literally stacks of pages with scribbled little list diagrams sketching the “before” and “after” situations for the various List operations. That’s what you want to have too before you hack the code!


There’s not much else to say, except for this: Pay extra attention to avoiding duplicated code! Here are a few examples of what we mean by that:

Please understand the assignment the right way: The goal is to end up with a List implementation that’s both simpler and more concise than what we gave you to start with.

No, you cannot subclass SentinelList from NodeList! No, you cannot use a NodeList inside your SentinelList! Your SentinelList has to stand on its own, relying only on the interfaces provided!


We’ll give you a few basic test-cases for the NodeList class on Piazza. Please be sure to read both ListTestBase.java and NodeListTest.java in detail before trying to modify them.

You’ll need to add more test cases to ListTestBase and those should apply to all lists regardless of how they are implemented. You’ll also need to add a class SentinelListTest.java whose job will be obvious once you have actually read the test cases we give you.

Be sure to test all methods and be sure to test all exceptions for error situations as well. Ideally you make the tests better first before you even start working on the SentinelList code.

Problem 2: List Reflection (10%)

This is not a programming problem; it’s a thinking and writing problem. You’ll have to write enough so we can tell that you understand the issues, but you shouldn’t write too much because that would also indicate that you don’t know what you’re doing. Please don’t ask us “How much is enough?” because there’s no right answer.

Your second task for this assignment is to write a reflection on the design we’ve come up with: The List interface itself and the Position interface clients use to “talk about” places in the data structure without “knowing” what those places really are. Here are some things you could write about, although there are probably a lot more:

Your reflection should go into your README file. Feel free to take your reflection in any direction you think is useful, the points above are simply suggestions. We certainly won’t “get mad” if you tell us that our List interface is really bad, just as long as your reasoning is sound. You want to convince us that you can think and write coherently about programming without doing any!


Please turn in a gzip-compressed tarball of your assignment; the filename should be cs226-assignment-number-name.tar.gz with number replaced by the number of this assignment (see above) and name replaced by the first part of the email address you used to register on Piazza. (For example, Peter would use cs226-assignment-3-phf.tar.gz for his submission of Assignment 3.) The tarball should contain no derived files whatsoever (i.e. no .class files, no .html files, etc.), but should allow building all derived files. Include a README file that briefly explains what your programs do and contains any other notes you want us to check out before grading; your answers to written problems should be in this file as well. Finally, make sure to include your name and email address in every file you turn in (well, in every file for which it makes sense to do so anyway)!


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 configuration file available on Piazza. Style also includes proper modularization of your code (into interfaces, classes, methods, using 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 (implied) axioms 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 programs cannot be built you will get no points whatsoever. If your programs cannot be built without warnings using javac -Xlint:all we will take off 10% (except if you document a very good reason; no, you cannot use the @SuppressWarnings annotation either). If your programs fail miserably even once, i.e. terminate with an exception of any kind, we will take off 10% (however we'll also take those 10% off if you're trying to be "excessively smart" by wrapping your whole program into a universal try-catch).