0

I created a code that basically just tests the approx. run-time of ArrayLists. However, the code will not compile if a line of code is moved spots. Here is the code that compiles.

public class ArrayTest {

    public static void main(String []args) {
        ArrayListTest();
    }

    private static void ArrayListTest() {
        //initializes random variable, accumulator and iterator and ArrayList
        Random rand = new Random();
        Float accum = 0F;
        ArrayList<Float> al = new ArrayList<>();

        //finds time of program pre-running
        long startTime = System.currentTimeMillis();

        //Populates ArrayList
        for (int i = 0; i < 20000000; i++) al.add(rand.nextFloat());

        //finds time of program post-running
        long endTime = System.currentTimeMillis();

        //Iterates through ArrayList, summing elements
        Iterator<Float> itr = al.iterator();
        while(itr.hasNext()) {
            accum += itr.next();
        }

        //Finds time of summing ArrayList
        long sumEndTime = System.currentTimeMillis() - endTime;

        //Prints meaningful conclusion
        System.out.print("ArrayList takes: " + (endTime - startTime) / 1000.0 + " seconds to initialize.\n");
        System.out.print("ArrayList takes: " + (sumEndTime / 1000.0) + " second to sum. \n");
    }
}

However, if you move the "Iterator itr = al.iterator();" to right after the declaration of the arraylist, it doesn't compile. Why is this?

  • 1
    "to the beginning of the code..." -- beginning of the code ***where*** exactly? – Hovercraft Full Of Eels Oct 17 '18 at 02:40
  • Obviously the line `Iterator itr = al.iterator();` has to be placed **after** `ArrayList al = new ArrayList<>();`, after you have created the array list. This just makes common sense, no? Otherwise it has no meaning. – Hovercraft Full Of Eels Oct 17 '18 at 02:42
  • And you can't create the iterator *before* you've added something to the ArrayList (without using the iterator itself), else it becomes invalidated, although this wouldn't cause a *compilation* error. – Hovercraft Full Of Eels Oct 17 '18 at 02:43
  • Edited for clarification. – Aidan O'Connor Oct 17 '18 at 02:51
  • 1
    ??? your code should still *compile* -- are you sure that this is happening, that the *compiler* won't allow you to create a class file? It looks to me like it will compile fine, and run, but when it runs it throws an exception. – Hovercraft Full Of Eels Oct 17 '18 at 02:53
  • Yes, I'm right and your question is misstated -- understand that moving the iterator creation to after array list creation will **not** cause a problem with compilation. And this is an important distinction since we're not looking at an entirely new type of problem. – Hovercraft Full Of Eels Oct 17 '18 at 02:57
  • Apologies, it does compile. Running it gives a ConcurrentModification error on the accum+= itr.next(); line. Saw the rest of your answer though. Thank! – Aidan O'Connor Oct 17 '18 at 03:01
  • 1
    I would say that 90% of question comments are ones that ask for clarity, so sorry if we seem to be nitpicking but programming language compilers are rigid and unforgiving, and so programmers must learn to be as well. Well, maybe not "unforgiving", but just a bit OCD, yes. – Hovercraft Full Of Eels Oct 17 '18 at 03:03
  • Please see edits to answer for more information – Hovercraft Full Of Eels Oct 17 '18 at 03:10

1 Answers1

1

Your code compiles just fine if you move the iterator creation to just after the ArrayList creation, and the code will run, but it will throw a ConcurrentModificationException because you're altering the list after creating the iterator and doing so not using the iterator. This will make the iterator in an invalid state.

If you create the iterator here:

ArrayList<Float> al = new ArrayList<>();
Iterator<Float> itr = al.iterator();

and then add to the ArrayList here:

// Populates ArrayList
for (int i = 0; i < 20000000; i++)
    al.add(rand.nextFloat());

the iterator is now trying to iterate through a collection that has changed since its creation, and this will cause the ConcurrentModificationException to be thrown here:

while (itr.hasNext()) {
    accum += itr.next();
}   

To get around this problem, only modify the collection with the iterator after you have created the iterator. This is mainly done when removing items from a collection. If you need to add items, you need to use a ListIterator, not a plain vanilla Iterator, either that or simply create your iterator after you have filled the collection.

e.g.,

ArrayList<Float> al = new ArrayList<>();
// Iterator<Float> itr = al.iterator();       
ListIterator<Float> listItr = al.listIterator();

long startTime = System.currentTimeMillis();

for (int i = 0; i < 20000000; i++) {
    // al.add(rand.nextFloat());
    listItr.add(rand.nextFloat());  // add with the ListIterator
}

long endTime = System.currentTimeMillis();

while (listItr.hasNext()) {
    accum += listItr.next();
}

Also you will want to read: What is the difference between run-time error and compiler error?
since this distinction is very important, especially when asking on this site. They are two very different broad categories of error, and for the most part, they don't overlap.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373