CrypticCabub CrypticCabub - 3 months ago 8x
Java Question

non-sequential index collection

I have a collection of objects that I need an Iterator over that can go through the objects in order (both forward and back) but can also go to any object when given the index of that object (which represents the lesson number of an assignment).

However, The index for each Object needs to be specifically predefined, in order, and there is no guarantee that my list of objects will have sequential indexes (I could have the indexes 0, 1, 6, 9 for example).

I currently have an ArrayList() which I am instantiating with the largestIndex I expect to be possible as the initial capacity, but I keep getting ArrayIndexOutOfBounds exception whenever I try to use the add(index, Object) method on my ArrayList

my code looks like this:

int largestIndex = unindexedAssignments.get(unindexedAssignments.size() - 1).getAssignmentID();
//index of 0 is ignored so add +1 to make sure we have enough space
assignments = new ArrayList<>(largestIndex + 1);

System.out.println("LargestIndex: " + (largestIndex + 1));

//populate assignments List
for(Assignment assignment : unindexedAssignments) {
//add the assignment to the list such that the index == the lesson number
System.out.println("adding assignment with index of " + assignment.getAssignmentID());
assignments.add(assignment.getAssignmentID(), assignment);

and the console spits out something like this (windows cmdpromt does not support copy/paste >_<):

largestIndex: 3
adding assignment with index of 1
java.lang.IndexOutOfBoundsException: Index 1, Size: 0
at java.util.ArrayList.rangeCheckForAdd(Unkown Source)
at java.util.ArrayList.add(Unknown Source)
(the rest of the stack trace pointing to the section of code I gave above ...)

I don't why the Size == 0 when I created what should have been an ArrayList with size 4?

A related question: am I misusing an ArrayList (and it's ListIterator) when there is a better default Java collection to use for this circumstance? The desired end-result is that my object has an Iterator object which is able to traverse back and forth and goto a specific location (right now I would just create a new ListIterator at the given index if it exist)

Thanks everyone!

Solution was to use a Navigable map and a cursor to track current location:

private final NavigableMap<Integer, StudentAssignment> studentAssignments = new TreeMap<>();

//the current location of the cursor on the studentAssignments navigable map (for use with next() and back())
//the value stored is the key value of the most recently viewed StudentAssignment
private Integer cursor;

private void next() {
if(cursor == null) {
cursor = studentAssignments.firstKey();
} else if(studentAssignments.higherKey(cursor) != null) {
cursor = studentAssignments.higherKey(cursor);
} else {
getUser().sendMessage(PREFIX + "No more assignments have been set after this one.");


Lists do not support sparse indices. If you want to add to an index that's past the end of the list, you have to create all of the intermediate indices as well.

Use a SortedMap. Maps are perfect for when you have non-contiguous indices. You can look up any assignment by its lesson number, and you can iterate over all the key-value pairs in order.

SortedMap<Integer, Assignment> assignments = new TreeMap<>();

for (Assignment assignment: unindexedAssignments) {
    assignments.put(assignment.getAssignmentID(), assignment);

You could also use Java 8 streaming syntax as an alternative to an explicit loop.

Map<Integer, Assignment> assignments =
        a -> a.getAssignmentID(),  // keys
        a -> a,                    // values
        (a, b) -> throw new IllegalStateException("duplicate lesson number"),
                                   // what to do if two items have the same key
        TreeMap::new               // map class constructor