/** * Copyright (c) 2011-2014, OpenIoT * * This file is part of OpenIoT. * * OpenIoT is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * OpenIoT is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with OpenIoT. If not, see <http://www.gnu.org/licenses/>. * * Contact: OpenIoT mailto: info@openiot.eu */ package org.openiot.cupus.common; import java.util.ConcurrentModificationException; import java.util.HashMap; import java.util.Iterator; import java.util.NoSuchElementException; /** * A minimalist Queue implementation that supports only the basic Queue methods.<br> * It doesn't allow null nor duplicate elements and guarantees no Exceptions, * only true/false returns.<br> * It is NOT serializable nor is it thread-safe nor does it have any of the * standard Object methods overriden (toString, hashCode and equals). * * It is basically a one-way-linked list with a head and tail pointer which is * enough for all basic queue methods. The contents of the queue are also backed * in a HashSet which, although increases memory footprint, makes it possible to * check for uniqness of an element in O(1) making it possible to unable * duplicate elements in the queue. * * The result is that all the methods have a time complexity of O(1). * * @author Eugen Rozic */ public class MinimalistLinkedHashQueue<E> implements Iterable<E> { private int capacity = Integer.MAX_VALUE; // default value private HashMap<E, Node<E>> backupMap; private Node<E> head = null; private Node<E> tail = null; private boolean dirty = false; /** * Sets the maximum capacity (after which offer returns false) to the given * value. */ public MinimalistLinkedHashQueue(int capacity) { this.capacity = capacity; this.backupMap = new HashMap<E, Node<E>>(capacity + 1); } public int size() { return backupMap.size(); } public boolean isFull() { return (size() >= capacity); } public boolean isEmpty() { return backupMap.isEmpty(); } /** * O(1) check - compliments of HashSet */ public boolean contains(E e) { return backupMap.containsKey(e); } /** * Adds the element at the end of this queue if it doesn't violate capacity, * if e!=null and queue !contains(e). * * @return True if the element was added, false if not. */ public boolean offer(E e) { if (size() >= capacity || e == null || contains(e)) return false; else this.dirty = true; Node<E> newNode = new Node<E>(tail, e, null); if (tail != null) // if newNode isn't the first element tail.next = newNode; if (head == null) // if newNode IS the first element head = newNode; tail = newNode; backupMap.put(e, newNode); return true; } /** * Removes the head of this queue, if there is one. * * @return The head if there was one, null if there wasn't. */ public E poll() { if (head == null) return null; else this.dirty = true; Node<E> toRemove = head; if (head == tail) { head = null; tail = null; } else { head = head.next; head.prev = null; } toRemove.next = toRemove.prev = null; backupMap.remove(toRemove.item); return toRemove.item; } /** * Returns the head of this queue, if there is one, but doesn't remove it * from the queue. * * @return The first element, null if there isn't one. */ public E peek() { if (head == null) return null; return head.item; } /** * Removes the given element from the queue, from any position. * * @return true if removed, false if not */ public boolean remove(E e) { Node<E> removedNode = null; if (e == null || (removedNode = backupMap.remove(e)) == null) return false; else this.dirty = true; // link prev to next if (removedNode.prev != null) removedNode.prev.next = removedNode.next; else head = removedNode.next; // becklink next to prev if (removedNode.next != null) removedNode.next.prev = removedNode.prev; else tail = removedNode.prev; // unlink the removed element's node (for GC) removedNode.next = removedNode.prev = null; return true; } @Override public Iterator<E> iterator() { return new Iter(); } /** * Represents a single element of the queue. */ private static class Node<E> { Node<E> prev; E item; Node<E> next; Node(Node<E> prev, E element, Node<E> next) { this.prev = prev; this.item = element; this.next = next; } } /** * For iterating over this queue from first element to last. <br> * Throws a ConcurrentmodificationException if modified while iterating... */ private class Iter implements Iterator<E> { Node<E> current; public Iter() { MinimalistLinkedHashQueue.this.dirty = false; current = head; } @Override public boolean hasNext() { if (dirty) throw new ConcurrentModificationException(); return (current != null); } @Override public E next() { if (dirty) throw new ConcurrentModificationException(); if (current == null) throw new NoSuchElementException(); E e = current.item; current = current.next; return e; } @Override public void remove() { throw new UnsupportedOperationException(); } } }