LinkedBlockingQueue with fast contains(Object o) method? - java

In a nutshell, I'm writing an application that needs a BlockingQueue implementation that provides both FIFO adds/removes, but also a fast contains method, as I'll be calling it a TON.
LinkedBlockingQueue gets me most of the way there, but it appears that its contains method runs in linear time, as it is based on AbstractQueue's contains method. I didn't see anything in the Java API that seemed to advertise an LBQ with fast contains out-of-the-box.
What makes things tougher is, my project is on a really severe time crunch (no, this isn't homework). I could do a quick-and-dirty LBQ extension with a HashSet underneath for fast contains, but I'm still going to have to test it, which could eat up a significant amount of man-hours. I'm wondering if there are any trusted/well-tested libraries out there that provide a LinkedBlockingQueue extension with a contains method that runs in O(1) time...? If not, any other suggestions are welcome.

Thanks again to all who provided input. I ended up coding my own HashedLinkedBlockingQueue implementation. Getting the synchronization correct by using the decorator pattern proved much more difficult than anticipated. Adding synchronization to the decorator caused deadlocks to occur under heavy load (particularly, put(E element) and take() introduced hold-and-wait conditions that previously didn't exist). When coupled with decreased performance because of unnecessary synchronization, it became apparent that I'd need to spend the time to get it right from scratch.
Performance in add/remove/contains is O(1) but comes with roughly double the synchronization cost of the original LinkedBlockingQueue - I say "roughly double" because the LBQ uses two locks - one for inserts and one for removes when the queue size is sufficiently large to allow concurrent modification of head and tail. My implementation uses a single lock, so removes must wait for adds to complete and vice versa. Here's the source code for the class - I have tested each method in both multi-threaded and single-threaded modes, but outside of using this class in my own application, I have not come up with a super-complicated, generalized test. Use at your own risk! Just because it works in my app doesn't mean there aren't still bugs:
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* Provides a single-lock queuing algorithm with fast contains(Object o) and
* remove(Object o), at the expense of higher synchronization cost when
* compared to {#link LinkedBlockingQueue}. This queue implementation does not
* allow for duplicate entries.
*
* <p>Use of this particular {#link BlockingQueue} implementation is encouraged
* when the cost of calling
* <code>{#link BlockingQueue#contains(Object o)}</code> or
* <code>{#link BlockingQueue#remove(Object o)}</code> outweighs the throughput
* benefit if using a {#link LinkedBlockingQueue}. This queue performs best
* when few threads require simultaneous access to it.
*
* <p>The basic operations this queue provides and their associated run times
* are as follows, where <i>n</i> is the number of elements in this queue and
* <i>m</i> is the number of elements in the specified collection, if any such
* collection is specified:
*
* <ul>
* <li><b>add(E element)</b> - <i>O(1)</i></li>
* <li><b>addAll(Collection<? extends E> c)</b> - <i>O(m)</i></li>
* <li><b>drainTo(Collection<? extends E> c, int maxElements)</b>
* - <i>O(maxElements*O(</i><code>c.add(Object o)</code><i>))</i></li>
* <li><b>contains(E element)</b> - <i>O(1)</i></li>
* <li><b>offer(E element)</b> - <i>O(1)</i></li>
* <li><b>poll()</b> - <i>O(1)</i></li>
* <li><b>remove(E element)</b> - <i>O(1)</i></li>
* <li><b>removeAll(Collection<? extends E> c)</b> - <i>O(m)</i></li>
* <li><b>retainAll(Collection<? extends E> c)</b> - <i>O(n*O(
* </i><code>c.contains(Object o)</code><i>))</i></li>
* </ul>
*
* #param <E> type of element this queue will handle. It is strongly
* recommended that the underlying element overrides <code>hashCode()</code>
* and <code>equals(Object o)</code> in an efficient manner.
*
* #author CodeBlind
*/
#SuppressWarnings("unused")
public class HashedLinkedBlockingQueue<E> implements BlockingQueue<E>{
/** Polling removes the head, offering adds to the tail. */
private Node head, tail;
/** Required for constant-time lookups and removals. */
private HashMap<E,Node> contents;
/** Allows the user to artificially limit the capacity of this queue. */
private final int maxCapacity;
//Constructors: -----------------------------------------------------------
/**
* Creates an empty queue with max capacity equal to
* {#link Integer#MAX_VALUE}.
*/
public HashedLinkedBlockingQueue(){ this(null,Integer.MAX_VALUE); }
/**
* Creates an empty queue with max capacity equals to the specified value.
* #param capacity (1 to {#link Integer#MAX_VALUE})
*/
public HashedLinkedBlockingQueue(int capacity){
this(null,Math.max(1,capacity));
}
/**
* Creates a new queue and initializes it to the contents of the specified
* collection, queued in the order returned by its iterator, with a max
* capacity of {#link Integer#MAX_VALUE}.
* #param c collection of elements to add
*/
public HashedLinkedBlockingQueue(Collection<? extends E> c){
this(c,Integer.MAX_VALUE);
}
/**
* Creates a new queue and initializes it to the contents of the specified
* collection, queued in the order returned by its iterator, with a max
* capacity equal to the specified value.
* #param c collection of elements to add
* #param capacity (1 to {#link Integer#MAX_VALUE})
*/
public HashedLinkedBlockingQueue(Collection<? extends E> c, int capacity){
maxCapacity = capacity;
contents = new HashMap<E,Node>();
if(c == null || c.isEmpty()){
head = null;
tail = null;
}
else for(E e : c) enqueue(e);
}
//Private helper methods: -------------------------------------------------
private E dequeue(){
if(contents.isEmpty()) return null;
Node n = head;
contents.remove(n.element);
if(contents.isEmpty()){
head = null;
tail = null;
}
else{
head.next.prev = null;
head = head.next;
n.next = null;
}
return n.element;
}
private void enqueue(E e){
if(contents.containsKey(e)) return;
Node n = new Node(e);
if(contents.isEmpty()){
head = n;
tail = n;
}
else{
tail.next = n;
n.prev = tail;
tail = n;
}
contents.put(e,n);
}
private void removeNode(Node n, boolean notify){
if(n == null) return;
if(n == head) dequeue();
else if(n == tail){
tail.prev.next = null;
tail = tail.prev;
n.prev = null;
}
else{
n.prev.next = n.next;
n.next.prev = n.prev;
n.prev = null;
n.next = null;
}
contents.remove(n.element);
if(notify) synchronized(this){ contents.notifyAll(); }
}
//Public instance methods: ------------------------------------------------
public void print(){
Node n = head;
int i = 1;
while(n != null){
System.out.println(i+": "+n);
n = n.next;
i++;
}
}
//Overridden methods: -----------------------------------------------------
#Override
public boolean add(E e){
synchronized(this){
if(remainingCapacity() < 1) throw new IllegalStateException();
enqueue(e);
contents.notifyAll();
}
return true;
}
#Override
public boolean addAll(Collection<? extends E> c){
boolean changed = true;
synchronized(this){
for(E e : c){
if(remainingCapacity() < 1) throw new IllegalStateException();
enqueue(e);
}
contents.notifyAll();
}
return changed;
}
#Override
public void clear(){
synchronized(this){
if(isEmpty()) return;
head = null;
tail = null;
contents.clear();
contents.notifyAll();
}
}
#Override
public boolean contains(Object o){
synchronized(this){ return contents.containsKey(o); }
}
#Override
public boolean containsAll(Collection<?> c) {
synchronized(this){
for(Object o : c) if(!contents.containsKey(o)) return false;
}
return true;
}
#Override
public int drainTo(Collection<? super E> c) {
return drainTo(c,maxCapacity);
}
#Override
public int drainTo(Collection<? super E> c, int maxElements) {
if(this == c) throw new IllegalArgumentException();
int transferred = 0;
synchronized(this){
while(!isEmpty() && transferred < maxElements)
if(c.add(dequeue())) transferred++;
if(transferred > 0) contents.notifyAll();
}
return transferred;
}
#Override
public E element(){
E e = peek();
if(e == null) throw new IllegalStateException();
return e;
}
#Override
public boolean isEmpty() {
synchronized(this){ return contents.isEmpty(); }
}
#Override
public Iterator<E> iterator(){ return new Itr(); }
#Override
public boolean offer(E e){
synchronized(this){
if(contents.containsKey(e)) return false;
enqueue(e);
contents.notifyAll();
}
return true;
}
#Override
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException{
long remainingSleep = -1;
long millis = unit.toMillis(timeout);
long methodCalled = System.currentTimeMillis();
synchronized(this){
while((remainingSleep =
(methodCalled+millis)-System.currentTimeMillis()) > 0 &&
(remainingCapacity() < 1 || contents.containsKey(e))){
contents.wait(remainingSleep);
}
if(remainingSleep < 1) return false;
enqueue(e);
contents.notifyAll();
}
return true;
}
#Override
public E peek(){
synchronized(this){ return (head != null) ? head.element : null; }
}
#Override
public E poll(){
synchronized(this){
E e = dequeue();
if(e != null) contents.notifyAll();
return e;
}
}
#Override
public E poll(long timeout, TimeUnit unit) throws InterruptedException{
E e = null;
long remainingSleep = -1;
long millis = unit.toMillis(timeout);
long methodCalled = System.currentTimeMillis();
synchronized(this){
e = dequeue();
while(e == null && (remainingSleep = (methodCalled+millis)-
System.currentTimeMillis()) > 0){
contents.wait(remainingSleep);
e = dequeue();
}
if(e == null) e = dequeue();
if(e != null) contents.notifyAll();
}
return e;
}
#Override
public void put(E e) throws InterruptedException{
synchronized(this){
while(remainingCapacity() < 1) contents.wait();
enqueue(e);
contents.notifyAll();
}
}
#Override
public int remainingCapacity(){ return maxCapacity-size(); }
#Override
public E remove(){
E e = poll();
if(e == null) throw new IllegalStateException();
return e;
}
#Override
public boolean remove(Object o){
synchronized(this){
Node n = contents.get(o);
if(n == null) return false;
removeNode(n,true);
}
return true;
}
#Override
public boolean removeAll(Collection<?> c){
if(this == c){
synchronized(this){
if(isEmpty()){
clear();
return true;
}
}
return false;
}
boolean changed = false;
synchronized(this){
for(Object o : c){
Node n = contents.get(o);
if(n == null) continue;
removeNode(n,false);
changed = true;
}
if(changed) contents.notifyAll();
}
return changed;
}
#Override
public boolean retainAll(Collection<?> c){
boolean changed = false;
if(this == c) return changed;
synchronized(this){
for(E e : new LinkedList<E>(contents.keySet())){
if(!c.contains(e)){
Node n = contents.get(e);
if(n != null){
removeNode(n,false);
changed = true;
}
}
}
if(changed) contents.notifyAll();
}
return changed;
}
#Override
public int size(){ synchronized(this){ return contents.size(); }}
#Override
public E take() throws InterruptedException{
synchronized(this){
while(contents.isEmpty()) contents.wait();
return dequeue();
}
}
#Override
public Object[] toArray(){
synchronized(this){ return toArray(new Object[size()]); }
}
#SuppressWarnings("unchecked")
#Override
public <T> T[] toArray(T[] a) {
synchronized(this){
//Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a != r)
return Arrays.copyOf(r, i);
r[i] = null; // null-terminate
return r;
}
r[i] = (T)it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
}
//Static helper methods: --------------------------------------------------
#SuppressWarnings("unchecked")
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
int i = r.length;
while (it.hasNext()) {
int cap = r.length;
if (i == cap) {
int newCap = ((cap / 2) + 1) * 3;
if (newCap <= cap) { // integer overflow
if (cap == Integer.MAX_VALUE)
throw new OutOfMemoryError
("Required array size too large");
newCap = Integer.MAX_VALUE;
}
r = Arrays.copyOf(r, newCap);
}
r[i++] = (T)it.next();
}
// trim if overallocated
return (i == r.length) ? r : Arrays.copyOf(r, i);
}
//Private inner classes: --------------------------------------------------
/**
* Provides a weak iterator that doesn't check for concurrent modification
* but also fails elegantly. A race condition exists when simultaneously
* iterating over the queue while the queue is being modified, but this is
* allowable per the Java specification for Iterators.
* #author CodeBlind
*/
private class Itr implements Iterator<E>{
private Node current;
private E currentElement;
private Itr(){
synchronized(HashedLinkedBlockingQueue.this){
current = head;
if(current != null) currentElement = current.element;
else currentElement = null;
}
}
#Override
public boolean hasNext(){
return currentElement != null;
}
#Override
public E next(){
if(currentElement == null) throw new NoSuchElementException();
synchronized(HashedLinkedBlockingQueue.this){
E e = currentElement;
current = current.next;
if(current == null || !contents.containsKey(current.element)){
current = null;
currentElement = null;
}
else currentElement = current.element;
return e;
}
}
#Override
public void remove(){
synchronized(HashedLinkedBlockingQueue.this){
if(current == null || !contents.containsKey(current.element))
throw new NoSuchElementException();
Node n = current;
current = current.next;
if(current != null && contents.containsKey(current.element))
currentElement = current.element;
else currentElement = null;
removeNode(n,true);
}
}
}
/**
* This class provides a simple implementation for a node in a double-
* linked list. It supports constant-time, in-place removals.
* #author CodeBlind
*/
private class Node{
private Node(E e){
element = e;
prev = null;
next = null;
}
private E element;
private Node prev, next;
#Override
public String toString(){
StringBuilder sb = new StringBuilder("Node[prev.element=");
if(prev == null) sb.append("null,element=");
else sb.append(prev.element+",element=");
sb.append(element+",next.element=");
if(next == null) sb.append("null]");
else sb.append(next.element+"]");
return sb.toString();
}
}
}

As others already said, using a hash-based structure is the solution.
I don't see any implementation on the Java Collection API.
But you can create a HashBlockingQueue decorator easily with the Guava class ForwardingBlockingQueue

Related

Trying to get this linked list started

Second programming class
So we have been tasked with a linked list, building each method from scratch.
Well I started on this day before yesterday and had a null pointer exception, I figured id iron it out later and continued.
Well after cutting my program down to nothing to find the culprit im left with code that SHOULD work as its copied from our lab (that worked).
If you guys think you can figure out why im getting a null pointer exception on my add method id greatly appreciate it and see if im doing the second constructor correctly. If I can get SOME traction on this to get started it would go allot easier but as is I cant even begin.
You will notice allot of blank methods, ill get to them once I can get my constructor + add method working
My code:
import java.util.Collection;
import java.util.Iterator;
/**
* Created by hhhh on 11/2/2014.
*/
public class Lset<R> implements Set151Interface<R> {
private Node head;
private int length;
/**In the first (following) constructor im trying to re use code and call my clear method.
*Should save space and make code look cleaner.
*/
public Lset(){
clear();
}
public Lset(Collection<? extends R> list){
this();
for (R object : list) {
add(object);
}
}
/**
* Copied from Lab7, this add method checks to see if there are more nodes than just the head.
* After the check if false, creates a new node and adds it to the end of the list.
* #param entry
* #return
*/
#Override
public boolean add(R entry) {
Node newNode = new Node(entry);
// empty list is handled differently from a non-empty list
if (head.next == null) {
head = newNode;
} else {
Node lastNode = getNodeAt(length - 1);
lastNode.next = newNode;
}
length++;
return true;
}
#Override
public void clear() {
this.length = 0;
this.head = null;
}
#Override
public boolean contains(Object o) {
return false;
}
#Override
public Iterator<R> iterator() {
return null;
}
#Override
public boolean containsAll(Collection<?> c) {
return false;
}
#Override
public boolean isEmpty() {
return false;
}
#Override
public boolean remove(Object o) {
return false;
}
#Override
public boolean addAll(Collection<? extends R> c) {
return false;
}
#Override
public boolean removeAll(Collection<?> c) {
return false;
}
#Override
public boolean retainAll(Collection<?> c) {
return false;
}
#Override
public int size() {
return length;
}
#Override
public Object[] toArray() {
return null;
}
#Override
public <T> T[] toArray(T[] array) {
return null;
}
/**
* Code used in Lab 7, getNodeAt uses the length field and starts at head to traverse array and arrive at the
* position desired.
* #param position
* #return
*/
private Node getNodeAt(int position) {
assert !isEmpty() && (position >= 0) && position < length;
Node cNode = head;
for (int i = 0; i < position; i++)
cNode = cNode.next;
assert cNode != null;
return cNode;
}
public String toString(){
String arrayString = "<";
for(int i = 0; i < length; i++){
String two = getNodeAt(i).toString();
arrayString += two;
if(i <= (length - 2)){
two = ", ";
arrayString += two;
}
}
arrayString += ">";
return arrayString;
}
//TODO comment better
public class Node {
/** Reference to the data */
public R data;
/** Reference to the next node is in the list */
public Node next;
/**
* Sets the data for this node.
* #param data data to be carried by this node.
*/
public Node(R data) {
this.data = data;
this.next = null;
}
/**
* Sets the data for the node and assigns the next node in the list.
* #param data data to be carried by this node.
* #param nextNode next node in the list.
*/
public Node(R data, Node nextNode) {
this.data = data;
this.next = nextNode;
}
/**
* Returns just the data portion of the node.
* #return The data portion of the node.
*/
public R getData() {
return this.data;
}
/**
* Modified just the data portion of the node.
* #param data new data to be contained within the node.
*/
public void setData(R data) {
this.data = data;
}
/**
* What node does this node point to.
* #return the node that this node points to or null if it does not
* point anywhere.
*/
public Node getNextNode() {
return this.next;
}
/**
* Change the node that this node points to.
* #param nextNode a new node for this node to point to.
*/
public void setNextNode(Node nextNode) {
this.next = nextNode;
}
/**
* Display the state of just the data portion of the node.
*/
public String toString() {
return this.data.toString();
}
}
}
This is the method in main thats killing it
private void testConstruction() {
System.out.println("\nTesting Constructor");
System.out.print("----------------------------------------");
System.out.println("----------------------------------------");
Set151Interface s = makeSet();
//added
s.add("Butterfinger");
test(s.size() == 0,
"size() should return 0: " + s.size());
test(s.toString().equals("<>"),
"toString returns \"<>\": " + s.toString());
ArrayList<String> temp = new ArrayList<String>();
temp.add("Butterfinger");
temp.add("Milky Way");
temp.add("Kit Kat");
temp.add("Three Muskateers");
Set151Interface s3 = makeSet(temp);
test(s3.size() == 4,
"size should return 4: " + s3.size());
test(s3.toString().equals("<Butterfinger, Milky Way, Kit Kat, Three Muskateers>"),
"toString should return\n "+
"\"<Butterfinger, Milky Way, Kit Kat, Three Muskateers>\":\n "
+ s3.toString());
}
as soon as butterfinger attempts to get added I get null pointer exception pointing to this line
if (head.next == null) {
You just declared private Node head; and it doesnt takes any value assigned . so the compiler throws NPE
Thanks for the help guys, I figured it out :).
On day one I had edited my driver (and forgot) once I re copied the driver everything works (so far) thanks again guys!

Made a Priority Queue, having problems with delMin and insert

I have made a Priority Queue class with an array list, but I am having trouble with the insert and delMin (delete minimum areas). I cannot create more functions and here is my code:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class MyMinPQ<E extends Comparable<E>> implements Iterable<E> {
private ArrayList<E> pq;
private int N;
public MyMinPQ() {
pq = new ArrayList<E>();
}
public E delMin(){
E minVal = min();
pq.remove(0);
N--;
return minVal;
}
public E min (){
if (isEmpty())
throw new NoSuchElementException();
return pq.get(0);
}
public void insert (E item){
for (int i = 0; i < N; i++){
pq.add(item);
if (pq.get(i) > pq.get(i+1)) {
E tmp = pq.get(i);
pq.set(i+1, tmp);
}
}
N++;
}
public boolean isEmpty() {
return N == 0;
}
public int size() {
return N;
}
public Iterator<E> iterator() {
return new Iterator<E>(){
int current = 0;
public boolean hasNext() {
return current != size();
}
public E next() {
if (hasNext())
return pq.get(current++);
else throw new NoSuchElementException( );
}
public void remove() {
throw new UnsupportedOperationException( );
}
};
}
}
At the insert portion of the code, I know that I have to sort the new additions to Arraylist but I am having issues with going about this. I tried to compare the values that is within the list, but eclipse does not allow it based on how I formatted it. When I use compareTo, it does not work with my iterator and everything goes into disarray.
My question is how can I go about modifying my insert function so it can sort new items in descending order? Will my delMin() also have to change because of it?
try this
public void insert(E item) {
int i = 0;
while (i < N && pq.get(i).compareTo(item) <= 0) {
i++;
}
N++;
}

Iterator method?

I'm implementing a list interface with links but since "ListADT" implements the Iterable interface. So, I have to have a method that produces an iterator which I'm not sure how to do. I tried using it as it is now and when I created an object for the linkedlist, and then call the iterator() method, I get an overflow. I know the method is supposed to produce an Iterator object but not sure how.
import java.util.Iterator;
public class LinkedList<T> implements ListADT<T>
{
protected int count;
protected LinearNode <T> head, tail;
private int modCount;
public LinkedList()
{
count =0;
head = tail= null;
}
public T removeFirst()
{
T result = head.getElement();
head = head.getNext();
count--;
return result;
}
public T removeLast()
{
// THROW EMPTY EXCEPTION
T result;
LinearNode <T> previous = null;
LinearNode <T> current = head;
while(!current.equals(tail))
{
previous = current;
current = current.getNext();
}
result = tail.getElement();
tail = previous;
tail.setNext(null);
count--;
return result;
}
public T remove(T element)
{
// throw exception
boolean found = false;
LinearNode <T> previous = null;
LinearNode <T> current = head;
while (current != null && !found)
{
if(element.equals(current.getElement()))
found = true;
else
{
previous = current;
current = current.getNext();
}
if (!found)
{
}
else if (current.equals(head))
{
head = current.getNext();
}
else if(current.equals(tail))
{
tail = previous;
tail.setNext(null);
}
else
previous.setNext(current.getNext());
}
count --;
return current.getElement();
}
public T first()
{
return head.getElement();
}
public T last()
{
return tail.getElement();
}
public boolean contains(T target)
{
boolean found = false;
LinearNode <T> previous = null;
LinearNode <T> current = head;
while (current != null && !found)
{
if(target.equals(current.getElement()))
found = true;
else
{
previous = current;
current = current.getNext();
}
}
return found;
}
public boolean isEmpty()
{
boolean result = false;
if( head == null && tail ==null)
{
result = true;
}
return result;
}
public int size()
{
return count;
}
public Iterator<T> iterator()
{
return this.iterator();
}
public String toString()
{
LinearNode <T> current = head;
String result ="";
String line = "";
int loopCount=0;
while(current != null)
{
loopCount++;
line = loopCount + "> " + (String) current.getElement() + "\n";
result = result + line;
current = current.getNext();
}
return result;
}
}
Your problem
You're getting an overflow because the line this.iterator() in your function public Iterator<T> iterator(), calls, you guessed it public Iterator<T> iterator().
Approach 1: The lazy way
If you don't plan on using the iterator for this class, (this looks like a programming assignment) you can always do the super super lazy.
public Iterator<T> iterator() {
throw new UnsupportedOperationException("Pffffft you don't need no iterator");
}
This approach is listed here just for completeness. Seeing as your linked list has no other way to access a random element in the middle without removing everything in front or behind it, I recommend you:
DO NOT DO THIS
Approach 2: The Correct Way
The thing about iterators is that they do a specific subset of what a list does, namely hasNext(), next(), and remove(). If you're unsure what those three methods do, I suggest you take a look at http://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html
You should create a public inner class.
public class LinkedList<T> implements ListADT<T> {
... stuff
private class MyIterator<T> implements Iterator<T> {
//It's best practice to explicitly store the head in the iterator
private LinearNode<T> head;
public MyIterator<T>(LinkedList<T>) {
...
}
#Override
public boolean hasNext() {
...
}
#Override
public T next() {
...
}
#Override
public void remove() {
...
}
}
public Iterator<T> iterator() {
return new MyIterator<T>(this);
}
}
Now if you're really clever, you can rewrite the rest of your code based on the iterator. Note:
DO THIS

Iterator on several sets

I am writing a Graph class,
I keep a HashMap in which ids of nodes (int values) are mapped to the associated nodes, and I'm using adjacency list approach to keep edges starting from a node (keeping them in form of a HashSet)
Note that : This graph is directed and unweighted,
I want to implement a method which returns an iterator over objects of class Edge :
When getting next on this iterator , one will get an object of class Edge which is created right when it's being traversed, and if there's no more neighbors for a node, it goes to the next node (order is not important) and if there's no more starting nodes (all are traversed), it finishes.
Any idea on how to implement this iterator on the edges without previously keeping the edges in Edge class objects ?
class Graph{
HashMap<Integer , GraphNode> nodes;
public Graph(){
nodes = new HashMap<Integer ,GraphNode>();
}
public boolean addEdge(GraphNode n1 , GraphNode n2){
if (!nodes.containsKey(n1) || !nodes.containsKey(n2))
return false;
return n1.addNeighbor(n2);
}
public boolean addNode(int id){
if (nodes.containsKey(id))
return false;
nodes.put(id , new GraphNode(id));
return true;
}
public boolean removeNode(GraphNode n1){
if (!nodes.containsKey(n1.content))
return false;
for (GraphNode m : n1.neighbors)
m.removeNeighbor(n1);
nodes.remove(n1);
return false;
}
public boolean removeEdge(GraphNode n1 , GraphNode n2){
if (!nodes.containsKey(n1) || !nodes.containsKey(n2))
return false;
return n1.removeNeighbor(n2);
}
public Iterator<GraphNode> NodeIterator(){
return nodes.values().iterator();
}
public Iterator<Edge> EdgeIterator(){
Iterator<GraphNode> itr = this.NodeIterator();
while (itr.hasNext){
GraphNode n = itr.next();
//......
}
}
}
class GraphNode{
HashSet<GraphNode> neighbors;
int content;
public GraphNode(int content){
this.content = content;
neighbors = new HashSet<GraphNode>();
}
boolean addNeighbor(GraphNode n){
if (neighbors.contains(n))
return false;
neighbors.add(n);
return true;
}
boolean removeNeighbor(GraphNode n){
if (!neighbors.contains(n))
return false;
neighbors.remove(n);
return true;
}
}
class Edge{
Node start , end;
public Edge(Node start , Node end){
this.start = start;
this.end = end;
}
}
I think something like this might work :
public Iterator<Edge> EdgeIterator(){
Iterator <Edge> edgeIter = new Iterator<Edge>() {
private Iterator<GraphNode> itr = this.NodeIterator();
private GraphNode currentNode;
... // additional private members as required
public void remove()
{
// you don't have to implement this method if you don't need to support
// this operation
}
public Edge next()
{
if (!hasNext())
throw new NoSuchElementException ();
return new Edge (x , y); // where you find x & y based on the current state
// of the iterator (kept in the private members of
// this instance)
}
public boolean hasNext()
{
return ?; // you return a boolean value based on the current state
// of the iterator (kept in the private members of
// this instance)
}
};
return edgeIter;
}
The EdgeIterator method creates an Iterator<Edge> and defines the methods of the Iterator interface (I left the implementation of these methods to you). The Iterator instance contains an instance of Iterator<GraphNode>, which it uses to iterate over the nodes.
You should add to the iterator some additional private members that keep track of the current node (the last node returned by the node iterator) and the current edge you are iterating on. Whenever you finish iterating over the edges of a node, you get the next node using itr.next() (after checking there is a next node available). next() of the edge iterator can construct the next Edge based on those private members.
As Eran said , I completed the code of the iterator methods ,
Do you think this one works ?
public Iterator<Edge> EdgeIterator(){
Iterator<Edge> edgeIter = new Iterator<Edge>() {
private Iterator<GraphNode> node_itr = NodeIterator();
private Iterator<GraphNode> neighbor_itr;
private GraphNode current_node;
private GraphNode current_neighbor;
public void remove()
{
if (current_node == null || current_neighbor == null)
return;
current_node.removeNeighbor(current_neighbor);
}
public Edge next()
{
if (neighbor_itr == null || !neighbor_itr.hasNext())
if (node_itr.hasNext()){
current_node = node_itr.next();
neighbor_itr = current_node.neighbors.iterator();
}else
return null;
current_neighbor = neighbor_itr.next();
return new Edge(current_node , current_neighbor);
}
public boolean hasNext()
{
if (neighbor_itr == null || !neighbor_itr.hasNext())
if (node_itr.hasNext())
return node_itr.next().neighbors.iterator().hasNext();
else
return false;
return true;
}
};
return edgeIter;
}
Update : The edited/working version :
public Iterator<Edge> EdgeIterator(){
Iterator<Edge> edgeIter = new Iterator<Edge>() {
private Iterator<GraphNode> node_itr = NodeIterator();
private Iterator<GraphNode> neighbor_itr;
private GraphNode current_node;
private GraphNode current_neighbor;
public void remove()
{
if (current_node == null || current_neighbor == null)
return;
current_node.removeNeighbor(current_neighbor);
}
private void moveNext(){
if (neighbor_itr == null || !neighbor_itr.hasNext()){
while (node_itr.hasNext()){
current_node = node_itr.next();
neighbor_itr = current_node.neighbors.iterator();
if (neighbor_itr.hasNext()){
break;
}
}
}
}
public Edge next()
{
moveNext();
current_neighbor = neighbor_itr.next();
return new Edge(current_node , current_neighbor);
}
public boolean hasNext()
{
moveNext();
return neighbor_itr.hasNext();
}
};
return edgeIter;
}

Java ListSet somewhere?

Looking for a insertion order collection that also allows efficient querying and subset views of positions (like sublist). Seems the most straightforward option for this would be to take the linked list approach of List, embed the nodes as map values and expose part or all of the list interface on the class.
Would someone bitch to Oracle about this? Having NavigableMap/Set added for sorted maps and sets and not having the far more common insertion order equivalent...
edit: please don't suggest LinkedHashSet - it doesn't have any way to query the position or to do a relative subset.
you mean like java.util.LinkedHashSet:
Hash table and linked list implementation of the Set interface, with
predictable iteration order. This implementation differs from HashSet
in that it maintains a doubly-linked list running through all of its
entries. This linked list defines the iteration ordering, which is the
order in which elements were inserted into the set (insertion-order).
Note that insertion order is not affected if an element is re-inserted
into the set. (An element e is reinserted into a set s if s.add(e) is
invoked when s.contains(e) would return true immediately prior to the
invocation.)
edit2: New final version
Here is a version only for sets with slightly adjusted function (divided into two, no longer accepts null as 'beginning of the map') that probably has less bugs
public class LinkedSet<E> implements Set<E> {
private LinkedHashMap<E, Integer> m = new LinkedHashMap<E, Integer>();
private int monoticallyIncreasing;
/**
* Returns true if the value target was added before (exclusive)
* limitElem in insertion order.
*
* If target or limit are not present on the set this method returns false
*
* #param limitElem a E that may be a element of the set or not.
* #return if target was added before limit (can be reset by removing and
* re-adding the target, that changes iteration order).
*/
public boolean containsBefore(E target, E limitElem) {
if (isEmpty()) {
return false;
}
Integer targetN = m.get(target);
if (targetN == null) {
return false;
}
Integer highN = m.get(limitElem);
if (highN == null) {
return false;
}
return targetN < highN;
}
/**
* Returns true if the value target was added after (exclusive)
* previousElem in insertion order.
*
* If target or previous are not present on the set this method returns false
*
* #param previousElem a E that may be a element of the set or not.
* #return if target was added before previous (can be reset by removing and
* re-adding the target, that changes iteration order).
*/
public boolean containsAfter(E target, E previousElem) {
if (isEmpty()) {
return false;
}
Integer targetN = m.get(target);
if (targetN == null) {
return false;
}
Integer low = m.get(previousElem);
if (low == null) {
return false;
}
return low < targetN;
}
#Override
public boolean add(E e) {
Integer pos = m.get(e);
if (pos == null) {
m.put(e, monoticallyIncreasing++);
return true;
}
return false;
}
#Override
public int size() {
return m.size();
}
#Override
public boolean isEmpty() {
return m.isEmpty();
}
#Override
public boolean contains(Object o) {
return m.containsKey(o);
}
#Override
public Object[] toArray() {
Object[] result = new Object[size()];
int i = 0;
for (E e : this) {
result[i++] = e;
}
return result;
}
#Override
#SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size) {
a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
}
int i = 0;
Object[] result = a;
for (E e : this) {
result[i++] = e;
}
if (a.length > size) {
//peculiar toArray contract where it doesn't care about the rest
a[size] = null;
}
return a;
}
#Override
public boolean remove(Object o) {
return m.remove(o) != null;
}
#Override
public boolean addAll(Collection<? extends E> c) {
boolean changed = false;
for (E e : c) {
changed |= add(e);
}
return changed;
}
#Override
public boolean containsAll(Collection<?> c) {
return m.keySet().containsAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
return m.keySet().retainAll(c);
}
#Override
public boolean removeAll(Collection<?> c) {
return m.keySet().removeAll(c);
}
#Override
public void clear() {
m.clear();
}
#Override
public Iterator<E> iterator() {
return m.keySet().iterator();
}
}

Categories