Does Java Iterator do a second traversal to remove? - java

If I use a linked list Iterator to find an element, does calling remove() cause the Iterator to traverse the list again? The code is below
//The list is LinkedList<String> list; and has already been populated
Iterator itar = list.iterator();
while(itar.hasNext())
{
if(itar.next().equalsIgnoreCase("Foo"))
itar.remove();
}

A LinkedList in Java is a doubly linked list, meaning each node has a reference to the next and previous in the list.
When the iterator does a remove, it has a reference to the current node and simply reassigns the references in the next and previous (updating the head and tail references for the list itself if needed).
So, no; there is no additional iteration performed.
Also worth noting is that Java is open source these days. You can view the source for LinkedList here with the code the iterator's remove() calls here
Edit to Add: As noted in the comments, an iterator for a singly-linked list implementation could easily keep track of the previous node it iterated over and perform the same operation.

No, iterators always only iterate once even if you call remove().
Removing an item from a linked list updates the relevant pointers without requiring any further iterations.

No Iterator does not start from beginning but it will point to the next element if exist.I want to make you aware about one important thing of Iterator that they are "Fail Fast" means if a collection is modified by one of its methods after an iterator is created for that collection, the iterator immediately becomes invalid and operations performed with the iterator after this point throw ConcurrentModificationExceptions.

Related

Get reference to the most recently added node in a java LinkedList

My aim is to delete a node somewhere in the middle of a Java LinkedList object, in O(1) time.
If I can get a reference to the node, I could probably do this myself without the need for a Java-provided method. But I cannot seem to find a way to get a reference to anything but the head of the list.
How can I get a reference to the last node in a Java LinkedList object? I wold then store these references in a map to use later.
Note: I know this is doable if I implement my own LinkedList, but is there a way to do it with Java's LinkedList class?
I would suggest actually changing your data structure to LinkedHashSet here instead of LinkedList. The reason for this is that LinkedHashSet#get() and remove() can lookup or delete any element by key in O(1) time. Also, a LinkedHashSet is implemented with a linked list running through the entries. The order of the entries while iterating the list are determined by the insertion order, so it behaves similarly to a LinkedList in that regard.
The closest thing you can get to a direct reference is an Iterator that points to a specific point.
If you call remove() on such an Iterator it should work in O(1):
LinkedList<Object> linkedList = ...;
Iterator<Object> it = linkedList.iterator();
while (it.hasNext()) {
if (matchesSomeCondition(it.next()) {
it.remove();
}
}
Note that this sample code definitely doesn't run in O(1), it's specifically just the remove() call that can have that efficiency. If you haven't already identified the node in some way (such as positioning an Iterator at that place), then you won't be able to remove an element from a LinkedList in O(1) time.
Edit and since you mention "last recently added" element then maybe a ListIterator is the thing to use, since it has an add() method. If you can efficiently implement all your adding/removing using a ListIterator, then you can keep the traversal operations over the LinkedList to a minimum. In fact, if you always use indexes to add/remove objects from your LinkedList then you loose a lot of its efficiency (since each add/remove call has to find the affected item first via traversal).

Iterating from the end of a list

I know that we can iterate over the list in the reverse order as follows:
List<Object> lst;
ListIterator<Object> i = lst.listIterator(lst.size());
But is it efficient if lst is a LinkedList? I mean when we obtain the ListIterator pointing to the end of the list, does the implementation iterate over the list from the begging to the list.size() position (takes O(n) time, where n is a size of the list)?
If it does, is there a way to avoid it?
The javadoc states that LinkedList is a doubly-linked list, so I would expect descendingIterator(), which return an iterator pointing to the tail of the list, to be O(1). Note that descendingIterator is from the Deque interface.
Now it is difficult to say whether the statement lst.listIterator(lst.size()) is also O(1), because it is not documented if listIterator method optimize the fact that the next element from lst.size() is the tail.
The documentation states that LinkedList is a "Doubly-linked list implementation of the List and Deque interfaces". So every element in the list has references to both the next AND the previous elements. So, the iterator should be as quick in the reverse order as it is in the natural order.
It doesn't iterate over the list to produce the iterator.
The best place to look for solutions to these is the Source Code.
if (index < (size >> 1)) {
next = header.next;
for (nextIndex=0; nextIndex<index; nextIndex++)
next = next.next;
} else {
next = header;
for (nextIndex=size; nextIndex>index; nextIndex--)
next = next.previous;
}
As you can see, it will try to reach the index using the shortest path either from the first node or last node.
LinkedList also implements Deque interface.
So if you implement it as
Deque list = new LinkedList();
Or if you additionally need the list methods
LinkedList list = new LinkedList();
You can use
list.descendingIterator();
Your code will not work, the index lst.size() is out of bounds, maybe you meant lst.size()-1. But still it is not a reverse iterator, it is a forward iterator that instead of beginning at 0 will begin at the element you specify. In this case you will read only the last element then reach the end.
LinkedList implements interface Deque which provides Deque.descendingIterator. In this case both instancing the iterator and moving to the next (previous) element are O(1) operations. In the first case it's because the Deque implementation keeps a reference to both the beginning and the end of the queue, in the second because LinkedList is a doubly-linked list, in which every element keeps a reference to both its successor and his predecessor.

Java LinkedList safest way to delete while iterating

I remember from a while back (I think it was some Java book) that the safest way to delete an element while iterating through a collection is using iterator.remove.
while(iterator.hasNext())
{
if(it.next().condition())
iterator.remove();
}
As I cannot find that reference and need a relative quick confirmation, can some java veteran confirm this?
This is the only legal way to structurally modify a LinkedList during iteration.
Any other way of removing an element from a linked list during iteration will (if you're lucky) throw a ConcurrentModificationException.
From the documentation:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the Iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException.

How does an Iterator throw ConcurrentModificationException on add

How does Iterator throw ConcurrentModificationException when we are adding some object after current node or removing some object after current node. Does Iterator maintain a copy or reference to the underlying collection?
The iterator maintains a reference to the underlying collection. If you add or remove an element, the iterator might be left at an impossible index, or the collection might change "out from underneath" the iterator.
Therefore, instead of letting the iterator get corrupted without letting you know, most collections do the courtesy of throwing a ConcurrentModificationException when you try to modify the collection while iterating, so you don't wind up with unpredictably corrupted iterators.
By contract, you are not allowed to modify the collection while iterating over it (except by using Iterator.remove() et al).
Instead of randomly failing when you do this, the collection is nice enough to keep track of how many times it's been modified, and throw ConcurrentModificationException when it detects concurrent modification.
That ConcurrentModificationException is probably your friend and you ought to learn to live with it. However, just for completeness:
There are non-Oracle collections out there that don't throw ConcurrentModificationException. They're faster (because they don't spend time checking) and, obviously, more flexible, but they require greater care when using.
Oracle has four (at last count) "Concurrent" classes that don't throw it either in java.util.concurrent (ConcurrentHashMap, ConcurrentLinkedQueue, ConcurrentSkipListMap, and ConcurrentSkipListSet). They're marginally slower than their non-concurrent equivalents, but they're thread-safe and they dont block. They won't scramble your data no matter what you do, but they won't stop you from scrambling it.
For removing you can use iterator.remove(), as follows:
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
Object object = iterator.next();
/* ... */
if (condition) {
iterator.remove();
}
For adding you can replace simple Iterator for ListIterator, as follows
ListIterator<Object> iterator = list.listIterator();
iterator.add(new Object());
Of course an iterator has a link to the underlying collection, this avoids the copy. If you look for example at the source code of ArrayList iterator (ListItr), you'll see it mostly has a link to the list and a cursor.
So, don't share an iterator between threads and don't modify a collection on which you're iterating.

Java ListIterator Performance

I was reading a thread here about the performance of java ArrayList and LinkedList. There is an answer from Mr Kevin Brock that reads the following.
"Linked list add is not always O(1)
[or this should say addLast() is
O(1)]. This is only true if done from
within a ListIterator. The add methods
in Java's LinkList implementation must
search through the list if additions
are not on the head or tail."
I din't understand what he meant by "only if done through ListIterator". Does it mean there is a data structure within the linkedlist that holds the reference of each index and as soon as we get the listiterator from a certain index, listiterator is returned straight away without walking through the list to find that index?
Thanks guys!
It means that iterator points to list nodes directly; and so access via get(int) will be O(N), but iterator.next() wil be O(1). Latter has direct reference and does not need to traverse anything; former will need to traverse from head of the list.
If you add to a LinkedList where the ListIterator is pointing to, it is O(1). This is the same as adding to the start of the LinkedList or the end of an ArrayList.
The comment refers to the two argument add method, [add(int,E)][1]. Assuming a linearly distributed index, then this will be O(n) as the list needs to be iterated through to find the appropriate node. It does not apply to add(E).
[1]: http://download.oracle.com/javase/6/docs/api/java/util/LinkedList.html#add(int, E)

Categories

Resources