iterating through a linkedhashmap but over a certain range - java

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.

Related

How to preserve the reference of elements from a collection for O(1) accessibility in Java

I'm new to Java. I tried to store iterators of LinkedList elements in a Map, and delete them later:
Map<Integer, Iterator<Integer>> map = new HashMap<>();
LinkedList<Integer> list = new LinkedList<>();
list.addFirst(1);
map.put(1, list.iterator());
list.addFirst(2);
map.put(2, list.iterator());
Iterator<Integer> iter = map.get(1);
iter.next();
iter.remove(); // list is supposed to be [2]
However, ConcurrentModificationException occurs. I think as soon as I add '2' in the list, the iterator of '1' expires, right?
In C++, list<int>::iterator represents the pointer of a node from a linked list, which stays constant and available whenever new nodes are inserted into the list. I'm a little bit confused about this in Java.
Sorry for the confusion. Now I know that Iterator is usually used in iterations, not to 'locate' an element, which is a bit different from that in C++.
I actually try to preserve the reference of elements from a linked list, so that the elements can be efficiently accessible in a complexity of O(1) instead of O(n).
Is there any related kind of Collection or Util? Or maybe I have to implement DeLinkedList and DeLinkedNode by myself. Thanks in advance.
If you want the ordering and O(1) insertion and removal of a linked list, coupled with the O(1) lookup of a hash table, LinkedHashSet is likely your best bet.
A LinkedHashSet is a Set that preserves insertion order. So you can do things like:
import java.util.LinkedHashSet;
...
LinkedHashSet<Integer> set = new LinkedHashSet();
set.add(2);
set.add(1);
set.remove(1);
A couple of caveats:
This is a Set, so you won't be able to keep more than one copy of an element.
There is no addFirst equivalent. Items are always added to the end.
Instead of using LinkedList, use ConcurrentLinkedDeque. The iterators will then work as you are hoping.
If you need the List functionality, as opposed to just iterators, you can very easily use the AbstractSequentialList class to create a List implementation from your ConcurrentLinkedDeque.

How to iterate over Set and add elements to it in java?

I want to iterate over a Set and if some condition meet I want to add elements to it.
While I am doing this, I am getting "ConcurrentModificationException".
When I looked for the answer, I found that in case of listIterator we have add() and remove() method but I can't use list as I also have to take care of duplicates.
Please suggest a way to achieve this.
Edit:
int[] A = {1,2,3,4,5,10,6,7,9};
Set<Integer> s = new HashSet<>();
s.add(1);
Iterator i = s.iterator();
while(i.hasNext()){
int temp = i.next();
int x = next element of array A;
if(x%2==0){
s.add(temp*x);
}
}
But it is throwing ConcurrentModificationException.
How to iterate over Set and add elements to it in java?
It cannot be done. Certainly, not with a HashSet or TreeSet. You will probably need to find an alternative way of coding your algorithm that doesn't rely on doing that.
The normal solution is to create a temporary list, add elements to that list, then when you have finished iterating use addAll to add the list elements to the set. But that won't work here because you appear to want your iterator to see the new elements that you have added.
A second approach would be use a ConcurrentHashMap and Collections::newSetFromMap instead of a HashSet. Iterating a concurrent collection won't give a ConcurrentModificationException. However, the flipside is that there are no guarantees that the iterator will see all of the elements that were added during the iteration. So this probably wouldn't work (reliably) for your example.

Sorting a list from the beginning

I have a list of objects that will take lots of adds/removes. I want the list to be sorted according to a certain function.
Right now, everytime I add a new object, I do:
list.add(obj1);
Collections.sort(list1, comparator);
Removing an object doesn't "unsort" the list, so I only need to do this for the add operation.
However, Collections.sort is O(>N) which is not very fast.
Is there any structure in java that allows me to keep a sorted list from the very beginning?
Forgot to mention
I tried to use TreeSet. It allows me to a pass a comparator which will be used for sorting but which will also be used to remove elements which is not what I want. I want it to be sorted but the remove functionality be identical to a list.
As Alencar proposes, use Collections.binarySearch(yourList, key, comparator) to find the correct insert position. This is far faster than looking up the whole list, since binarySearch only needs log2(size of list) queries to find the correct insertion position.
So, if your insert code was
void sortedInsert(List<T> list, T value, Comparator<T> comparator) {
int pos=0;
ListIterator<T> it=list.listIterator();
while (it.hasNext() && comparator.compare(value, it.next()) < 0) pos ++;
if (pos < it.previousIndex()) it.previous();
it.add(value);
}
... and a faster version would be ...
void sortedInsert2(List<T> list, T value, Comparator<T> comparator) {
int pos = Collections.binarySearch(list, value, comparator);
if (pos < 0) {
pos = -pos -1; // returns negatives if not found; see javadoc
}
list.insert(value, pos);
}
Note that the difference may not be that great, because inserting into a non-linked list requires shifting elements up. So if the underlying list is an ArrayList, copying, on average, half the elements one place to the right to make place for the new element results in O(n) extra time per copy. And if the list is linked, you still need to pay that O(n) penalty, but this time to perform the binary search -- in that case, it would probably be better to use the first version.
Note that the 1st version uses a ListIterator just in case you decide to use linked lists. For ArrayLists, it would be easier to use list.get(pos) and list.add(pos, value), but those are a very bad idea in the context of iterating linked lists.
Can't you add the object in the "right" place? already sorted?
You said that every time you add a new object you sort the list/collection.
Maybe you can use a Binary Search to find the exact index you need to insert the new value or use your comparator to find.

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 Linked List: Removing Largest Element Using Iterator

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

Categories

Resources