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.
Related
I have a scenario that I am iterating over a set using iterator. Now I want to remove 1st element while my iterator is on 2nd element. How can I do it. I know Set is unordered and there is nothing like first or second element but my question is I want to remove an element which is not being currently returned by Iterator.next
I dont want to convert this set to list and using listIterator.
I dont want to collect all objects to be removed in other set and call remove all
I cant store it and remove after the iteration
sample code.
Set<MyObject> mySet = new HashSet<MyObject>();
mySet.add(MyObject1);
mySet.add(MyObject2);
...
Iterator itr = mySet.iterator();
while(itr.hasNext())
{
// Now iterator is at second element and I want to remove first element
}
Given the constraints as you have stated them, I don't think that there is solution to the problem.
The Iterator.remove() method will only remove the "current" element.
You have excluded "remembering" objects and removing them from the HashSet in a second pass / phase.
Schemes that involve using two iterators simultaneously and removing using one of them will result in CCMEs on the second one.
The three approaches that you suggested (but then excluded) would all work. I think the 2nd one would be the most performant.
Another idea would be to implement a new hash table-based Set type which has an Iterator with an extra remove operation. (You could start with the source code of HashSet etcetera, rename the class and then modify it to do what you need.)
Set.iterator() returns a java.lang.Iterator. This iterator only provides methods to remove the current element and to iterate forward.
So if you don't want to convert your set, using only Iterator you cannot remove the previous element.
What you can do for example is that you collect the elements you want to remove, and after you iterated through the whole set, you remove the collected elements after, e.g. with Set.removeAll(removableCollection):
List<MyObject> removableList = new ArrayList<>();
MyObject previous;
Iterator<MyObject> itr = mySet.iterator();
while (itr.hasNext()) {
MyObject current = itr.next();
// If you find you want to remove the previous element:
if (someCondition)
removableList.add(previous);
previous = current;
}
mySet.removeAll(removeableList);
HashSet is unordered and javadoc clearly states that Iterator's remove method Removes from the underlying collection the last element returned by this iterator (optional operation). So the answer is no through an iterator.Since HashSet contains unique elements,you can use Set.remove(Object) after traversing the first element,in thios case you dont even need to go to the 2nd element
HashSet<K> hs;// you HashSet containing unique elements
if(!hs.isEmpty())
{
hs.remove(hs.iterator().next());
}
Just remember HashSet is unordered and there is no such thing as 1st or 2nd element
Alternately,you should use LinkedHashSet which gives you an ordered Set based on insertion order
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.
So i know how to iterate through a whole linkedhashmap from the beginning, but what if I want to only link through a certain portion from it? IE: i want to start from the end and go only 4 elements back. How would I do that and is it possible?
What you are searching for is a ListIterator which would allow you to iterate backwards in a list. Unfortunately, LinkedHashMap does not hold a reference towards the previous element, and thus does not provide this iterator.
So, you end up with two solutions. One, you implement the method to find the X last elements: you hold, let's say an array (a circular buffer) of size X and keep there the last X elements you have seen. This solution is rather inefficient if you call this method frequently and for X much smaller than the size of your map.
A second solution is to keep a HashMap instead of a LinkedHashMap and an extra List to maintain the insertion order. E.g. an ArrayList or a LinkedList which provide a ListIterator and thus, backwards iteration.
You could use the ListIterator for this, by doing something like this.
List list = new ArrayList<>(map.keySet());
ListIterator li = list.listIterator(list.size());
while (li.hasPrevious()) {
System.out.println(map.get(li.previous()));
}
Since the LinkedHashMap maintains the order, you could simply create a list from the keys which are going to be in order as well. Get a ListIterator from the last index, so that you can traverse backwards having a counter(which I've not shown) to iterator till the no. of elements required.
You have to extend standard implementation and override methods that return appropriate iterator to your own.
Iterator<K> newKeyIterator() { return new KeyIterator(); }
Iterator<V> newValueIterator() { return new ValueIterator(); }
Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }
LinkedHashMap.Entry is a doubly linked list, so you can go forward and backward as well.
LinkedHashMap.LinkedHashIterator is a base iterator for LinkedHashMap. Make what you need based on it.
This article states that there's "No random access" in LinkedList. Can anybody explain this to me?
Given
LinkedList<String> l = new LinkedList<>();
Then I can use,
l.get(n);
Given this, why does the article say "No random access"?
Random access here means, that you cannot directly access any element in a linked list similar to an array.
In linked list you have to traverse each element (link) starting from the head and then you can access that element.
l.get(n);
This method also works the same way in the background. It traverse from the head and then retrieves the nth element
Random Access means that you can find in constant time the i-th element. More specifically, it doesn´t depend on the size of your list, or if you're accessing the first element, the last, or one in the middle.
With Linked Lists
You have to traverse all the elements from the first one to the i-th one to find the i-th one. Hence, it takes much more time to get the last element than the first one. Hence, this is not random access.
With Arrays
The elements in your array are stored contiguously in memory. Hence, if you know the address A of the first element, and the elements have each a size S, you know directly the address of the i-th element: A + i*S. This operation takes the same time for any element in your array, and is enough to retrieve it. Hence, arrays are random access.
You will start from head and traverse to n and it's not random access!!!
And here is the method of traversing from head to N
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
When we say that about a java collection it means it doesn't implements RandomAccess interface
You can read further about it here
RandomAccess
The javadoc writes:
Doubly-linked list implementation of the List and Deque interfaces. Implements all optional list operations, and permits all elements (including null).
All of the operations perform as could be expected for a doubly-linked list. Operations that index into the list will traverse the list from the beginning or the end, whichever is closer to the specified index.
That is, even though a LinkedList offers a method to access the i-th element, that method will internally walk along the list until it reaches that element, and is therefore inefficient.
That's probably what your tutorial calls "no random access".
An ArrayList, in constrast, is based on arrays, where the i-th element can be accessed directly, or as its javadoc puts it:
The size, isEmpty, get, set, iterator, and listIterator operations run in constant time. The add operation runs in amortized constant time, that is, adding n elements requires O(n) time. All of the other operations run in linear time (roughly speaking). The constant factor is low compared to that for the LinkedList implementation.
Generally speaking, java.util.LinkedList are rarely ever used, as an ArrayList requires less memory, can be iterated over more quickly, and supports efficient access by index. That doesn't mean that linked lists (the data structure) are useless, it's just that their main advantage (the ability to keep a reference to a list element, to possibly remove that element or add new elements near it) is not permitted by java.util.LinkedList, as iterators are invalidated by concurrent modification.
To cut a long story short: You can probably forget about LinkedList, and simply use an ArrayList whenever you need a List.
If you use get(n) it skips past the first n-1 elements in the list. if index = n/2 then the search starts from the end of the list.
how can I remove the largest element from a linkedlist in java? I know I can use get() or remove() functions to retrieve / remove the elements. But I want to make it efficient. I want to use iterator. Do you have any idea how i can do it? Note that I dont want to create my own linkedlist. I dont even want to sort my linkedlist.
Can I do a linear search for this? If so, how can I keep track of the pointer (or iterator) that points to the largest element. Any help will be appreciated.
A linked list (unless sorted) is not the best structure for what you are trying to do here as you have no other option than do a linear search and remove the biggest element
Integer biggest = Integer.MIN_VALUE;
for(Integer e : myList){
if(biggest < e)
biggest = e;
}
myList.remove(biggest);
this would be O(n), even though you have to scan again to remove the biggest, this second scan would be avoidable if you did your own LinkedList because the java.util.LinkedList hides its Entry class and does not allow you to modify the pointers; but that would be the wrong way to optimize, because if instead you use a heap like structure, in java that would be PriorityQueue class, you could get O(log(n)) and reduce the code to:
return myPriorityQueue.poll();
if you know what the largest element is you can do it like so
Iterator<Integer> it = list.iterator();
Integer toRemove;
while(it.hasNext()){
if(toRemove.compareTo(it.next()==0){
it.remove();
break;
}
}
or use list.removeFirstOccurrence(toRemove)
but LinkedList implementations don't allow for copying iterators to point to specific elements for later removal