Search code examples
javaarraylistdeque

Anything faster than iterating ArrayList from the end side?


Using this Java code:

    // create the items list
    List<Item> items = new ArrayList<Item>();
    // ... (add some elements into the list so that it is not empty)

    // iterating ArrayList<Item> from right to left we find the position
    // based on the `if` condition satisfied for an item property
    int pos = 0;
    for (int j = items.size() - 1; j >= 0; j--) {
        Item item = items.get(j);
        if (item.property <= 7) {
            pos = j + 1; break;
        }
    }

    // add new item on the found above position
    Item it = new Item();
    if (pos == items.size()) {
        items.add(it);
    } else {
        items.add(pos, it);
    }

I'm wodering if this statement Item item = items.get(j); will take some extra time for execution because of the ArrayList used. For example, imagine we need to add the new item to the end, then by calling get() on the items list will iterate it from the left only, which is redundant. I would expect to use Deque structure instead of ArrayList.

What could you recommend, maybe I'm wrong at all, because new elements can be added in the beginning as well, though the goal is to iterate from the right side to the left.


Solution

  • For an ArrayList, the operations get(i), iteration and add/remove at the (right) end are cheap (basically O(1) or amortized O(1)). However, adding/removing elements at the other (left) end are very expensive and involve copying the whole backing array each time.

    So if you need to add and remove at both ends, be sure to use an ArrayDeque. This is basically as cheap as an ArrayList for all operations, but supports adding at both ends in a cheap way.

    Note that if you only want to add/remove at the beginning of the list, not at the end, you can still use an ArrayList and just it "backwards" (so when you want to add something in the beginning, you just add it at the end, and when you want to iterate right-to-left, you actually iterate left-to-right).

    Inserting elements with the add(Object, int) method is O(n) for both data structures (all elements to the right of the inserted element need to be moved in the array).

    An alternative could be to use LinkedList (which also implements Deque). You just need to be sure that you do not use get(int) or any other method where you pass an index as an int, because this is O(n). However, add/remove/insert operations are O(1) at all positions in the list (as long as you already have an iterator pointing to the position). So for the loop in your code, call .listIterator() on the list, iterate appropriately and use the ListIterator.add(Object) method to insert the element. This would be cheapest solution for your code overall.

    Edit

    I hadn't noticed that ArrayDeque doesn't offer the method to insert elements in the middle (although it could, just like ArrayList). (Thanks A.H.) So if you really need all these operations (insertion in the beginning, middle and end), use LinkedList and ListIterator.