How to iterate PriorityBlockingQueue in order - java

I have a PriorityBlockingQueue<Shirts> that uses a FIFO (first in first out) comparator in addition to other custom conditions. While obeying the priority, I need to pull out only the Green shirts. I understand the objective with a Queue is that they are intended for items to be "taken off of the top". The iterator() doesn't obey the order, so I can't use that. Should I be using a different queue altogether? How can I iterate over these items.

PriorityQueue.iterator() doesn't obey the order but retrieval operations poll, remove, peek, and element access the element at the head of the queue. So, taking into consideration that PriorityBlockingQueue cannot have null elements, you can iterate the elements in order this way
PriorityBlockingQueue<Shirts> q = ...
for (Shirts shirts; (shirts = q.poll()) != null; ) {
...
}
Another way
Shirts[] a = q.toArray(new Shirts[q.size()]);
Arrays.sort(a);
now you can iterate in both direction

Taking a look at the javadoc, you should be able to use the "take" method.
public E take()
throws InterruptedException
Description copied from interface: BlockingQueue
Retrieves and removes the head of this queue, waiting if necessary until an element becomes available.
Just throw a for loop around the queue and voila! You can iterate with no problems. :)
Assuming the queue is strictly FIFO
You can then use "put" to put the item back into the queue if it is not the green shirt
public void put(E e)
Inserts the specified element into this priority queue. As the queue is unbounded, this method will never block.
This way the non-green shirts will still be in the queue in order (once the loop is finished).
The way you have it setup
Since you are using a PriorityBlockingQueue, putting the objects right back in the queue could eventually lead to the same item being pulled off the queue each time around the loop. You would need to create a new queue to put the item back into once it is checked to see if it is a green shirt.
Example code
PriorityBlockingQueue queue = new PriorityBlockingQueue();
PriorityBlockingQueue backupqueue = new PriorityBlockingQueue();
queue.put("A");
queue.put("B");
queue.put("C");
queue.put("D");
List<String> saveList = new ArrayList<String>();
int initialSize = queue.size();
for(int i = 0; i < initialSize; i++){
String element = queue.take();
if(element.equals("C")){
saveList.add(element);
} else {
backupqueue.put(element);
}
}

Related

Understanding how priorityqueue works [duplicate]

I have a Priority Queue in which I add a Node object to, where the Nodes should be sorted by a value that they contain. For some reason, the priority queue will not sort the Nodes on add. If anyone can see something wrong with this or has any guidance, I appreciate it. Here is a brief example:
PriorityQueue<Node> PQ = new PriorityQueue<Node>();
//for each entry create a node and add it to the PriorityQueue
for(Entry<Character,Integer> entry : entries){
PQ.add(new Node(entry.getKey(),entry.getValue(), true));
}
here is the node's compareTo method:
#Override
public int compareTo(Node n) {
if(n.frequency.intValue() > this.frequency.intValue()) return -1;
else if(n.frequency.intValue() == this.frequency.intValue()) return 0;
else return 1;
}
I guess you expect PriorityQueue to return elements in particular order when you iterate it. However, PriorityQueue doesn't provide such a behaviour, because it's implemented as a priority heap rather than sorted list. From javadoc:
The Iterator provided in method iterator() is not guaranteed to traverse the elements of the priority queue in any particular order. If you need ordered traversal, consider using Arrays.sort(pq.toArray()).
The only guarantee provided by PriorityQueue is that poll(), peek(), etc return the least element. If you need ordered iteration of elements, use some other collection such as TreeSet.
For anyone looking how to iterate the queue following the order, this can be achieved by using poll or remove.
while (!queue.isEmpty())
System.out.println(queue.poll());
while (!queue.isEmpty())
System.out.println(queue.remove());
The only diference between poll() and remove(), is that poll returns null when is empty and remove throws a NoSuchElementException.

How come my priority queue with my self-defined comparator is not giving the right order? [duplicate]

I have a Priority Queue in which I add a Node object to, where the Nodes should be sorted by a value that they contain. For some reason, the priority queue will not sort the Nodes on add. If anyone can see something wrong with this or has any guidance, I appreciate it. Here is a brief example:
PriorityQueue<Node> PQ = new PriorityQueue<Node>();
//for each entry create a node and add it to the PriorityQueue
for(Entry<Character,Integer> entry : entries){
PQ.add(new Node(entry.getKey(),entry.getValue(), true));
}
here is the node's compareTo method:
#Override
public int compareTo(Node n) {
if(n.frequency.intValue() > this.frequency.intValue()) return -1;
else if(n.frequency.intValue() == this.frequency.intValue()) return 0;
else return 1;
}
I guess you expect PriorityQueue to return elements in particular order when you iterate it. However, PriorityQueue doesn't provide such a behaviour, because it's implemented as a priority heap rather than sorted list. From javadoc:
The Iterator provided in method iterator() is not guaranteed to traverse the elements of the priority queue in any particular order. If you need ordered traversal, consider using Arrays.sort(pq.toArray()).
The only guarantee provided by PriorityQueue is that poll(), peek(), etc return the least element. If you need ordered iteration of elements, use some other collection such as TreeSet.
For anyone looking how to iterate the queue following the order, this can be achieved by using poll or remove.
while (!queue.isEmpty())
System.out.println(queue.poll());
while (!queue.isEmpty())
System.out.println(queue.remove());
The only diference between poll() and remove(), is that poll returns null when is empty and remove throws a NoSuchElementException.

Iterating through a list and allowing it to have an item removed at the same time (NOT ITERATOR)

ATTENTION: I CANNOT know if doSomething will remove the element or not. This is an exceptional case that my data structure needs to handle.
My problem is simples:
int size = list.size();
for(int i = 0; i < size; i++) {
MyObj mo = list.get(i);
mo.doSomething();
}
Now if doSomething() remove mo from the list, I eventually get an ArrayIndexOutOfBounds because the list has now shrunk.
What data structure should I use to allow iteration with the possibility of removing? I can NOT use an iterator here, in other words, I can NOT make doSomething return a boolean and call iterator.remove(). The data structure has to somehow handle the situation and continue to iterator through the rest of the elements still there.
EDIT: I CANNOT know if doSomething will remove the element or not. This is an exceptional case that my data structure needs to handle.
Part II => Making a smart listeners notifier to avoid code duplication everywhere
You can use an ArrayList, for example, as long as you update the index and size when something is removed.
List<MyObj> list = new ArrayList<MyObj>();
int size = list.size();
for(int i = 0; i < size; i++) {
MyObj mo = list.get(i);
mo.doSomething();
if (size > list.size()) {
size = list.size();
i--;
}
}
This only works if the item removed is the last one examined. For other changes to the list you will have to have more complicated logic.
What data structure should I use to allow iteration with the possibility of removing?
The simplest option is to take a copy of the list and iterate over that instead:
List<MyObj> copy = new ArrayList<MyObj>(list);
for (MyObj mo : copy) {
mo.doSomething();
}
Now it doesn't matter whether or not anything removes an idea from the original list - that won't change the copy of the list.
Another option is to use CopyOnWriteArrayList. You can then just iterate and remove or add items at will:
The "snapshot" style iterator method uses a reference to the state of the array at the point that the iterator was created. This array never changes during the lifetime of the iterator, so interference is impossible and the iterator is guaranteed not to throw ConcurrentModificationException. The iterator will not reflect additions, removals, or changes to the list since the iterator was created.
I think you should change you doSomething(). If mo.doSomething() can remove mo from l, you mo must know your l.
You can change the code like this:
Create a valid flag, inside of your MyObj. Only listen if valid.
while(list.hasNext()) {
MyObj mo = list.next()
if(mo.isValid()){
mo.doSomething();
} else {
list.remove();
}
}

Is it Possible to create a Queue for HashMap set?

Right now I am trying to create a producer/consumer thread, the producer thread goes through all possible combinations of letters and creates their respective MD5 hashes. Then each combination and its hash is put into the HashMap<String,String>. Now in my consumer thread I want to be able to use the Queue<> collection on the hashmap so my consumer thread may call poll() etc thus removing values atc like a Queue but still giving me the capability of seeing both the combination and its hash when calling poll() How would I go about doing this? I have the HashMap but dont know how to 'make' or cast it as a Queue.
Thanks.
You should not use a HashMap without handling the thread-safety of your code. Else, you may end with a Live-lock.
To be able to iterate your Map with the order in which keys were inserted, you can use a LinkedHashMap.
Map m = Collections.synchronizedMap(new LinkedHashMap(...));
The producer would push entries like this (nothing special):
m.put(key, object)
The consumer would poll entries like this:
while (someCondition) {
Map.Entry nextEntry = null;
// This block is equivalent to polling
{
synchronized(s) {
Iterator i = s.iterator(); // Must be in the synchronized block
if (i.hasNext()) {
nextEntry = i.next();
i.remove();
}
}
}
if (nextEntry != null) {
// Process the entry
...
} else {
// Sleep for some time
...
}
// process
}
The LinkedHashMap type is like a combination of a HashMap and a Queue - it stores key/value pairs, but also remembers the order in which they were inserted. This might be exactly the type you're looking for. There is no explicit poll() function, but if you get an iterator over the LinkedHashMap you will visit the elements in the order in which they were added. You could probably then write a function like this:
public <KeyType, ValueType> KeyType first(LinkedHashMap<KeyType, ValueType> map) {
assert !map.isEmpty();
return map.iterator().next();
}
which will give you back the first element. Just make sure to synchronize appropriately.
Alternatively, you could consider just storing key/value pairs inside a Queue by defining a helper class Pair and then storing Pairs in the queue.
Hope this helps!
I suggest you create a Queue of EntrySet -
Queue<EntrySet<String,String>> queue = new SynchronousQueue<EntrySet<String,String>>();
for (EntrySet<String,String> entry:map.entrySet()) {
queue.add(entry);
}
You can consider using another type of queue, which lets you put the elements, and only the prdocuer waits in case of non empty such as LinkedBlockingQueue.
The producer will then be able to recompose a map based on the EntrySet objects, if needed.

Add element to the end of a Java LinkedList while iterating it

I'm iterating a java.util.LinkedList and in some cases I add an element to it.
LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
Schedule schedule = iterator.next();
if(condition)
iterator.add(new Schedule());
}
The problem is that if I start for example with one item, the new item is added before the next iterator.next() call and the iteration exits.
How can I append the item at the end of the LinkedList while iterating?
Please don't tell me to use another list and iterate it after the first because it doesn't solve my problem correctly.
Assuming you don't have a hard requirement to use an iterator then you can just shelve it and 'iterate' over the list by index instead:
LinkedList<Schedule> list;
for (int i = 0; i < list.size(); i++) {
final Schedule schedule = list.get(i);
if(condition)
list.add(new Schedule());
}
If you cannot use another list, you could solve your problem by keeping a count of the number of elements you processed via the iterator and compare that to the original size of the list: all new element will be at the end of the list, so you can end your loop when you have reached the original size.
LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
int origSize = queue.size();
int currCount = 0;
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
++currCount;
if (currCount >= origSize) {
break; // reached the end of the original collection
}
Schedule schedule = iterator.next();
if(condition)
iterator.add(new Schedule());
}
You could also use an extra list to keep track of the new elements and add that to the original list after the processing is over:
LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
LinkedList<Schedule> addQueue = new LinkedList<Schedule>();
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
Schedule schedule = iterator.next();
if(condition)
addQueue.add(new Schedule());
}
queue.addAll(addQueue);
Also, note that iterator.add()
Inserts the specified element into the list (optional operation). The element is inserted immediately before the next element that would be returned by next, if any, and after the next element that would be returned by previous, if any. (If the list contains no elements, the new element becomes the sole element on the list.) The new element is inserted before the implicit cursor: a subsequent call to next would be unaffected, and a subsequent call to previous would return the new element. (This call increases by one the value that would be returned by a call to nextIndex or previousIndex.)
so if you have more than one elements in the list, it will not add the new ones to the end, but between the current one and the one returned by next(). If you indeed want to place the new elements at the end of the list, use queue.add(...)
In general, it is not advisable to modify a collection while traversing it via an iterator, so I suggest you use the second approach (collect the extra elements in a separate list and add them to the original at the end)
As others have suggested, there is no efficient, off-the-shelf support for end-insertion during iteration in the current Collections framework. One suggestion involved rewriting the iterator. But I say, why not go a couple steps further?
Use reflection to modify the accessibility of the Node class, which gives you references to the double links, so you don't need to start iteration over with get(index), which should not be used in performant code.
Inherit/derive from LinkedList and override judiciously.
This is a no-brainer, but in the same spirit as 2 above, since the JDK is open-source, borrow the source as needed and write your own implementation.
In either case, I really feel this feature should be provided by Java LinkedList API.
How can I append the item at the end of the LinkedList while
iterating?
public void addWork(Scheduler scheduler)
{
synchronized(scheduler)
{
queue.addLast(scheduler);
}
}
and you can use queue.removeFirst() to deal with item in the queue from top-to-down.
public synchronized Scheduler getWork()
{
return queue.removeFirst();
}
Edited.
The requirements to add while iterating and include the added items in the iteration can only be met if you don't use an iterator for iteration since there is no way to recompute the state of your iterator every time an element is added. If you accept the less efficient get method to do your iteration, the problem is trivial. For example
LinkedList<Schedule> queue = new LinkedList<Schedule>(){{add(new Schedule());add(new Schedule());add(new Schedule());}};
int i = 0;
// queue.size() is evaluated every iteration
while (i < queue.size()) {
Schedule schedule = queue.get(i);
if(i++ % 2 == 0)
queue.add(new Schedule());
}
System.out.println(queue.size());
prints 6 as expected.

Categories

Resources