Back to 111 Main Page

Mobius strip

Assignment 15

Task

Write a simple to-do list that is kept sorted in terms of urgency.

Concepts: Objects, instance variables
Textbook: 3.1, 4.1 - 4.2

Steps

Here is a simple to-do list program: ToDoList.java. This program allows the user to add a new task to the end of the list, or remove the item at the beginning of the list.

You are going to write a more advanced to-do list where each item also has an associated urgency (an int between 1 and 10). To do this, you must use this class:

Just save this file in the same directory as your UsernameA15.java file and Java will find it. You do not need to submit it or include it in your file. Tamarin already has a copy of this file, so do not change your copy or your program won't compile with Tamarin's version.

Therefore, instead of storing the to-do list as a String[], you will use a ToDoItem[]. This array should start at size 5, but its size should be doubled each time its capacity is exceeded (as in A14).

In addition, your to-do list program must do the following things:

  • At the start of the program (and again after each operation), print the contents of the list and a menu of options. Menu options are 1) Add item, 2) Remove first item, and 0) Quit. Your program may accept additional inputs (such as "a", "r", or "q"), but must at least accept 1, 2, and 0 for the menu choices.
  • When printing the list, you must list the tasks one-per-line. Each task must be followed by its urgency rating, though this can be set off in parantheses, brackets, or the like. The printed list must be sorted in urgency order, with higher urgencies listed first.
  • When adding a new item, the program must ask for (only) the task (a String) and then the urgency (an int). If there is an urgency tie with an item already in the list, the new item should be placed after all the other items with the same urgency already in the list.
  • When removing, no additional information should be asked of the user: just remove the first item in the list. Print the task of the item just removed as confirmation.
  • When quitting, the program should end gracefully.
  • At no time should the program crash when given a String instead of an int or if given an int out of range.

You may write your to-do list from scratch. However, you are also welcome to start with the sample ToDoList program and modify it. Just remember to update any documentation and put your name on it.

Sample Output

TO-DO LIST:

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 2
Could not remove first item because list is empty.

TO-DO LIST:

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 1
Enter the task to do: Wash the dog
Enter the task's urgency (1 to 10): high
Urgency must be a number.  Please try adding the item again.

TO-DO LIST:

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: add
Please enter a number corresponding to one of the options.

TO-DO LIST:

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 1
Enter the task to do: Wash the dog
Enter the task's urgency (1 to 10): 4

TO-DO LIST:
1. Wash the dog [4]

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 1
Enter the task to do: Do homework
Enter the task's urgency (1 to 10): 8

TO-DO LIST:
1. Do homework [8]
2. Wash the dog [4]

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 1
Enter the task to do: Test program
Enter the task's urgency (1 to 10): 6

TO-DO LIST:
1. Do homework [8]
2. Test program [6]
3. Wash the dog [4]

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 1
Enter the task to do: Sleep
Enter the task's urgency (1 to 10): 4

TO-DO LIST:
1. Do homework [8]
2. Test program [6]
3. Wash the dog [4]
4. Sleep [4]

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 2
Removed "Do homework" from list.

TO-DO LIST:
1. Test program [6]
2. Wash the dog [4]
3. Sleep [4]

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 2
Removed "Test program" from list.

TO-DO LIST:
1. Wash the dog [4]
2. Sleep [4]

Options:
1. Add an item.
2. Remove the first item.
0. Quit.
Choose an option: 0

Thanks for using the To-Do list!

Things to consider

In terms of functionality, it'd be nice if the user could delete any item from the list (not just the first), or change the urgency of items already in the list. However, this mostly just requires a lot of array processing, and the focus of this assignment is instead meant to be on using objects.

Notice how you are using ToDoItem to bundle the task and urgency of each item together. When you sort these objects using their urgencies, the tasks go along with the object and are automatically sorted as well. Consider the advantage of this behavior if we were sorting objects with lots of fields--such as a email messages, song tracks, etc.

Notice too how your main method is probably getting pretty long and complicated. It would help to break the problem down into methods, but this can be tricky with our current setup. For example, to add an item to the array in a static method, we'd need to pass the array, the count, and the item to add to the array, and then return the new count. However, as we'll see over the next couple weeks, we could make the ToDoList an object itself. Then each ToDoList could have its own state (instance variables such as the array and the count) and behaviors (methods like add and remove). Then we wouldn't need to pass these details to the methods, and we could even have lists of ToDoLists.

What to Submit

Upload your UsernameA14.java file to Tamarin.

Grading [5 points]

1 - Compiles + Coding Standards
Your program compiles successfully (no errors). Your code follows Java coding standards.
1 - Array of ToDoItems
Your program uses a ToDoItem[] to store details entered by the user (0.75). You initially declare this array to be of size 5, but you double its size (and copy over all elements) each time the array capacity is exceeded (0.25).
0.5 - Printing list
Your program first displays the contents of the list time each time it asks for user input. The list shows all items, one per line, each including that item's urgency rating.
1 - Printed list is sorted
Items are inserted into the list in urgency order with high urgency before low urgency (0.75). In the case of a tie, the new item comes after all items with the same urgency already in the list (0.25).
0.5 - Adding
Menu option 1 lets you add an item. User must specify any string and then an urgency rating (int) for that item. The item is then added to the list. (Note that failing to do with will affect your printing and removing scores.)
0.5 - Removing
Menu option 2 removes the first item in the list (0.25), printing its task in a confirmation message (0.25).
0.5 - Misc
Menu option 0 quits the program (0.25). Program doesn't crash from invalid input (0.25).

FAQs

Any chance of seeing that code you did on the board in lab (Sections 001 and 002) again?
We wrote a HighScore class and then a Game class that uses it.
How do I add a new item as an object to the array?
First of all, the overview: You should only have 1 array of ToDoItems. The idea is that each ToDoItem object stored in that array will then contain both details (the task and the urgency) for that single item. However, when you first create the array, it will be empty. That is, it doesn't actually have any ToDoItems in it. You need to create a new ToDoItem for each item you add to the array when you add it.

Next, the (imperfect) analogy: You can think of the todo array as an empty egg carton--a number of empty slots in linear order. What you are going to do is get the yolk (a String task) and an egg white (an int urgency) from the user. You can't just put these directly into the empty egg carton, which is designed to hold whole eggs. (You'd make a big mess if you did!) So you'll create a shell (a new ToDoItem object), and then put the yolk and egg white into the shell. Then you can put the whole egg (ToDoItem) into the egg carton (array).

Finally, the relevant code: In your add section, you first need to create a new ToDoItem object:

  ToDoItem toAdd = new ToDoItem();
Then you can load it with data:
  toAdd.task = //...String read from the user
  toAdd.urgency = //...int read from the user
And finally add that whole object to the array:
  todo[count] = toAdd;
  count++;

The caveat: Of course, this is just one way to do it. You could forgo the toAdd variable by just using todo[count] throughout:

  todo[count] = new ToDoItem();
  todo[count].task = //....
  todo[count].urgency = //....
  count++;
(This can be trickier than the first approach when you consider shifting the array, however.) And of course this is just the code for creating the object and putting in into the array; there's a lot more involved with getting the info from the user and actually finding where to put the object into the array (or sorting it afterwards).
I'm getting a NullPointerException! What's up with that?
Your error message probably looks something like this (at the end of your output):
Exception in thread "main" java.lang.NullPointerException
        at ZtomaszeA15.main(ZtomaszeA15.java:49)

This message tells me that the error (a NullPointerException) came while running my main method, on line 49 of ZtomaszeA15.java. So now I can go to line 49 to see what's wrong.

A NullPointerException comes from trying to access a field in an object that doesn't exist. That is, some variable or array cell does not contain an object, but you're trying to access a instance variable (or instance method) in that object anyway. (See the previous FAQ for more.)

This can come from a variety of sources. Some examples:

  todo[count].task = usersTask;
This will generate an NPE if you do not have the line todo[count] = new ToDoItem(); before this line.
  for (int i = 0; i < todo.length; i++) {
    if (todo[i].urgency < toAdd.urgency) {
If the todo array is not full, this would crash on the if line, but the problem is actually in the for loop. This loop will go through every object in the array, checking the urgency of each. If there are no objects in the array yet, however, todo[i].urgency will produce a crash, even the first time through when i is 0. Instead, I probably meant to loop up to count, rather than todo.length.

These are just some examples; your context may be different. But an NPE always comes from trying to access some part of an object that isn't there.

I can add three items to my list, and it prints out three items, but they are all identical: the last item I added!
Short answer: You did not create a new ToDoItem for each item you added. In short, you need the words new ToDoItem() in your add case. See the previous FAQ.

Long answer: Remember that arrays of objects just hold references to those objects. The objects themselves are located somewhere in the heap part of memory. What this means is, if you only execute new ToDoItem() one time in your code (that is, it's not in a loop, called each time you add something), there's only a single ToDoItem object in memory.

This happens when you declare ToDoItem toAdd = new ToDoItem(); in the same place as your todo array, before the rest of your code. This will create a ToDoItem object at some memory address (lets call that address A) and assign that address to toAdd. The first time you add an item (say, Wash the dog [4]), you load this object with data, and store its address in the array. Memory now looks like this:

toAdd: A
todo [A - - - -]  (where - means null)
count: 1

A: [task: "Wash the dog"; urgency: 4]

If you then add a second item (say, Do homework [8]), but do not create another object, then todo still contains the address A. You assign todo.task and todo.urgency, but this is just overwriting the data that was already in that object. You then add the value in todo (still A) to the array. Now you have this in memory:

toAdd: A
todo [A A - - -]  (where - means null)
count: 2

A: [task: "Do homework"; urgency: 8]

And so on. Each time the user "adds" another item, you just overwrite the contents in your single object and add another reference to that single object to your array.

Instead, if you assign a new ToDoItem() to toAdd for each addition (and thus create another object in memory), you would instead have this state:

toAdd: B
todo [A B - - -]  (where - means null)
count: 2

A: [task: "Wash the dog"; urgency: 4]
B: [task: "Do homework"; urgency: 8]
Do we have to add items in sorted order? Or can we just add the new item to the end and then sort the array, as we did for A14?
You can add and then sort. That would be fine.

For those of you that used the sort method from java.util.Arrays: I'm afraid you'll have to sort the array manually this time, since it is an array of objects instead of primitives. (Note: It would be possible to use one of the sort methods if the objects you are sorting have implemented the Comparable interface. Since you can't change ToDoItem, this is not an option. The other option would be to write a separate class that implements the Comparator interface, and then use an instance of this other class to sort the objects in the array. You are free to do this, but it requires writing an extra class that implements an interface--an advanced concept not normally covered in 111.)

Can I use a java.util.ArrayList instead of an array to hold the ToDoItems?
No. You need more experience with arrays first. We'll cover ArrayLists in lab in a couple weeks, and then you'll be required to use those for one assignment (probably A18). After that you'll be free to choose which one to use in later assignments.


~ztomasze Index : TA Details: ICS111: A15
http://www2.hawaii.edu/~ztomasze
Last Edited: 27 Oct 2009
©2009 by Z. Tomaszewski.