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();
}
}
Related
I need to create priority set/array that bases on:
public interface IListener
{
public Priority getPriority();
public enum Priority
{
HIGHEST,
HIGH,
NORMAL,
LOW,
LOWEST;
}
}
IListeners are stored in:
HashMap<Class<? extends IListener>, Set<IListener>> listeners = new HashMap<>();
I am looking to make method that will always add IListener in 1st place after its Priority group.
Example:
Given Set contains some IListeners with this order.
{ HIGHEST, HIGHEST, HIGH, HIGH, LOW, LOW, LOW, LOWEST }
Adding listener with Priority == HIGH would result in:
{ HIGHEST, HIGHEST, HIGH, HIGH, HIGH, LOW, LOW, LOW, LOWEST }
Bold one being newly added.
I know I could just iterate and add at 1st "free slot", but question is rather - does Java provide some good-looking (maybe better?) solutions? Might be just for future reference.
As indicated in the comments, I don't think there is any collection in the JDK that exactly meets your requirements.
IListenerSet is an implementation of Set that meets your needs. The iterator always returns the elements in order of priority. If two elements have the same priority, they are returned in the order they were put into the set. The set supports addition and removal. The iterator supports the remove() method. The set cannot contain null, and throws a NullPointerException if you try to add null. The set cannot contain an IListener for which getPriority() returns null, and throws an IllegalArgumentException if you try to add such an element.
public final class IListenerSet<T extends IListener> extends AbstractSet<T> {
private final Map<IListener.Priority, Set<T>> map;
public IListenerSet() {
map = new EnumMap<>(IListener.Priority.class);
for (IListener.Priority p : IListener.Priority.values())
map.put(p, new LinkedHashSet<>());
}
public IListenerSet(Collection<? extends T> collection) {
this();
addAll(collection);
}
#Override
public int size() {
int size = 0;
for (Set<T> set : map.values())
size += set.size();
return size;
}
#Override
public boolean contains(Object o) {
if (!(o instanceof IListener))
return false;
IListener listener = (IListener) o;
IListener.Priority p = listener.getPriority();
return p != null && map.get(p).contains(listener);
}
#Override
public boolean add(T listener) {
IListener.Priority p = listener.getPriority();
if (p == null)
throw new IllegalArgumentException();
return map.get(p).add(listener);
}
#Override
public boolean remove(Object o) {
if (!(o instanceof IListener))
return false;
IListener listener = (IListener) o;
IListener.Priority p = listener.getPriority();
return p != null && map.get(p).remove(listener);
}
#Override
public void clear() {
for (Set<T> set : map.values())
set.clear();
}
#Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private Iterator<T> iterator = map.get(IListener.Priority.values()[0]).iterator();
private int nextIndex = 1;
private Iterator<T> nextIterator = null;
#Override
public boolean hasNext() {
if (iterator.hasNext() || nextIterator != null)
return true;
IListener.Priority[] priorities = IListener.Priority.values();
while (nextIndex < priorities.length) {
Set<T> set = map.get(priorities[nextIndex++]);
if (!set.isEmpty()) {
nextIterator = set.iterator();
return true;
}
}
return false;
}
#Override
public T next() {
if (iterator.hasNext())
return iterator.next();
if (!hasNext())
throw new NoSuchElementException();
iterator = nextIterator;
nextIterator = null;
return iterator.next();
}
#Override
public void remove() {
iterator.remove();
}
};
}
}
An alternative approach is to use TreeSet with custom comparator and automatically assign autogenerated ids to the elements added to the set, so the latter elements always get bigger id which can be used in comparison:
public class IListenerSet extends AbstractSet<IListener> {
private long maxId = 0;
private final Map<IListener, Long> ids = new HashMap<>();
private final Set<IListener> set = new TreeSet<>(new Comparator<IListener>() {
#Override
public int compare(IListener o1, IListener o2) {
int res = o1.getPriority().compareTo(o2.getPriority());
if(res == 0)
res = ids.get(o1).compareTo(ids.get(o2));
return res;
}
});
#Override
public Iterator<IListener> iterator() {
return new Iterator<IListener>() {
Iterator<IListener> it = set.iterator();
private IListener e;
#Override
public boolean hasNext() {
return it.hasNext();
}
#Override
public IListener next() {
this.e = it.next();
return e;
}
#Override
public void remove() {
it.remove();
ids.remove(e);
}
};
}
#Override
public int size() {
return set.size();
}
#Override
public boolean contains(Object o) {
return ids.containsKey(o);
}
#Override
public boolean add(IListener e) {
if(ids.get(e) != null)
return false;
// assign new id and store it in the internal map
ids.put(e, ++maxId);
return set.add(e);
}
#Override
public boolean remove(Object o) {
if(!ids.containsKey(o)) return false;
set.remove(o);
return true;
}
#Override
public void clear() {
ids.clear();
set.clear();
}
}
Keep it easy:
You can combine several kinds of collections:
A LinkedHashSet allows you to store items by ordering them by insertion order (and with no repeated items).
A TreeMap allows you to map keys and values ordering them according to the keys.
Thus, you can declare this combination:
TreeMap<IListener.Priority, LinkedHashSet<IListener>> listenersByPriority=new TreeMap<IListener.Priority, LinkedHashSet<IListener>>(new PriorityComparator());
... and encapsulate it in a proper abstraction to manage it:
public class ListenerManager
{
private final TreeMap<IListener.Priority, LinkedHashSet<IListener>> listenersByPriority=new TreeMap<IListener.Priority, LinkedHashSet<IListener>>();
private int size;
public void addListener(IListener listener)
{
synchronized (listenersByPriority)
{
LinkedHashSet<IListener> list=listenersByPriority.get(listener.getPriority());
if (list == null)
{
list=new LinkedHashSet<IListener>();
listenersByPriority.put(listener.getPriority(), list);
}
list.add(listener);
size++;
}
}
public Iterator<IListener> iterator()
{
synchronized (listenersByPriority)
{
List<IListener> output=new ArrayList<IListener>(size);
for (LinkedHashSet<IListener> list : listenersByPriority.values())
{
for (IListener listener : list)
{
output.add(listener);
}
}
return output.iterator();
}
}
}
When declaring a TreeMap, it is usually necessary an specific implementation of Comparator coupled to the key class, but it is not necessary in this case, because enums are already comparable by its ordinal. (thanks to Paul Boddington). And the ordinal of each enum item is the position it is placed in the declaration.
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
i have some problems with some of my methods for my MultiSet class.
This is a tester, and MultiSet class should get the output: "Succes!" if it works correctly.
This is the tester:
public class MultiSetTest {
public static void main(String[] args) {
MultiSet<String> a = new MultiSet<String>();
MultiSet<String> b = new MultiSet<String>();
a.add("Foo");
a.add("Bar");
a.add("Foo");
System.out.println("a:" + a); // test toString
b.add("Bar");
b.add("Foo");
b.add("Bar");
b.add("Foo");
System.out.println("b:" + b);
assert !a.equals(b) : "Failed test 1!"; // test equals
assert b.remove("Bar") : "Failed test 2!"; // test remove
assert a.equals(b) : "Failed test 3!";
for(String s : a) { // test iterator
assert b.remove(s) : "Failed test 4!";
}
assert b.size() == 0 : "Failed test 5!";
Set<String> baseSet = new HashSet<String>(a);
assert baseSet.size()==2 : "Failed test 6!";
b = new MultiSet<String>(a);
assert a.equals(b) : "Failed test 7!";
try {
assert false;
System.out.println("Please enable assertions!");
}
catch(AssertionError e) {
System.out.println("Success!");
}
}
}
And my Multiset class:
public class MultiSet<E> extends AbstractCollection<E>
{
private int size = 0;
private Map<E, Integer> values = new HashMap<E, Integer>();
public MultiSet()
{
}
public MultiSet(Collection<E> c)
{
addAll(c);
}
public boolean add()
{
return false;
}
public boolean remove()
{
return false;
}
public Iterator<E> iterator()
{
return new Iterator<E>()
{
private Iterator<E> iterator = values.keySet().iterator();
private int remaining = 0;
private E current = null;
public boolean hasNext()
{
return remaining > 0 || iterator.hasNext();
}
public E next()
{
if (remaining == 0)
{
current = iterator.next();
remaining = values.get(current);
}
remaining--;
return current;
}
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
public boolean equals(Object object)
{
if (this == object) return true;
if (this == null) return false;
if (this.getClass() != object.getClass()) return false;
MultiSet<E> o = (MultiSet<E>) object;
return o.values.equals(values);
}
public int hashCode()
{
return values.hashCode()*163 + new Integer(size).hashCode()*389;
}
public String toString()
{
String res = "";
for (E e : values.keySet());
//res = ???;
return getClass().getName() + res;
}
public int size()
{
return size;
}
}
Maybe if you could help me on the way with either add or remove, then i can probably work the other one out.
Also, my equals doesn't appear to work correctly,
and I'm unsure about how to work out "res" at String toString. Don't mind my return statement, i'll throw in some brackets etc. later to make it look good.
Thank you for your help.
// Chris
Why not on of use well-tested Google Guavas's Multisets instead of reinventing the wheel? You can choose one of many implementations:
ConcurrentHashMultiset,
EnumMultiset,
ForwardingMultiset,
HashMultiset,
ImmutableMultiset,
LinkedHashMultiset,
TreeMultiset
what should cover your use case or - if you really want - implement Multiset interface by yourself, looking into sources of default implemntations.
EDIT:
Your implementation breaks Collecion interface contract - you cannot return false for add(E e). Read Collection docs:
boolean add(E e)
Parameters:
e - element whose presence in this collection is to be ensured
Returns:
true if this collection changed as a result of the call
Throws:
UnsupportedOperationException - if the add operation is not supported by this collection
If you want to use read-only Multiset use either ImmutableMultiset (more specifically ImmutableMultiset.copyOf(Iterable)) or implement Multiset
's interface add(E e) method throwing UnsupportedOperationException.
Is it even possible?
Say you have
private Set<String> names = new LinkedHashSet<String>();
and Strings are "Mike", "John", "Karen".
Is it possible to get "1" in return to "what's the index of "John" without iteration?
The following works fine .. with this question i wonder if there is a better way
for (String s : names) {
++i;
if (s.equals(someRandomInputString)) {
break;
}
}
The Set interface doesn't have something like as an indexOf() method. You'd really need to iterate over it or to use the List interface instead which offers an indexOf() method.
If you would like to, converting Set to List is pretty trivial, it should be a matter of passing the Set through the constructor of the List implementation. E.g.
List<String> nameList = new ArrayList<String>(nameSet);
// ...
I don't believe so, but you could create a LinkedHashSetWithIndex wrapper class that would do the iteration for you, or keep a separate table with the indexes of each entry, if the performance decrease is acceptable for your use case.
It is generally not possible for a Set to return the index because it's not necessarily well defined for the particular Set implementation. For example it says in the HashSet documentation
It makes no guarantees as to the iteration order of the set; in particular, it does not guarantee that the order will remain constant over time.
So you shouldn't say the type is Set when what you actually expect is a Set implementing som order.
Here is an implementation that does insertions, removals, retainings, backed by an arraylist to achieve o(1) on get(index).
/**
* #Author Mo. Joseph
*
* Allows you to call get with o(1) instead of o(n) to get an instance by index
*/
public static final class $IndexLinkedHashSet<E> extends LinkedHashSet<E> {
private final ArrayList<E> list = new ArrayList<>();
public $IndexLinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public $IndexLinkedHashSet() {
super();
}
public $IndexLinkedHashSet(int initialCapacity) {
super(initialCapacity);
}
public $IndexLinkedHashSet(Collection<? extends E> c) {
super(c);
}
#Override
public synchronized boolean add(E e) {
if ( super.add(e) ) {
return list.add(e);
}
return false;
}
#Override
public synchronized boolean remove(Object o) {
if ( super.remove(o) ) {
return list.remove(o);
}
return false;
}
#Override
public synchronized void clear() {
super.clear();
list.clear();
}
public synchronized E get(int index) {
return list.get(index);
}
#Override
public synchronized boolean removeAll(Collection<?> c) {
if ( super.removeAll(c) ) {
return list.removeAll(c);
}
return true;
}
#Override
public synchronized boolean retainAll(Collection<?> c) {
if ( super.retainAll(c) ) {
return list.retainAll(c);
}
return false;
}
/**
* Copied from super class
*/
#Override
public synchronized boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
}
To test it:
public static void main(String[] args) {
$IndexLinkedHashSet<String> abc = new $IndexLinkedHashSet<String>();
abc.add("8");
abc.add("8");
abc.add("8");
abc.add("2");
abc.add("3");
abc.add("4");
abc.add("1");
abc.add("5");
abc.add("8");
System.out.println("Size: " + abc.size());
int i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
abc.remove("8");
abc.remove("5");
System.out.println("Size: " + abc.size());
i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
abc.clear();
System.out.println("Size: " + abc.size());
i = 0;
while ( i < abc.size()) {
System.out.println( abc.get(i) );
i++;
}
}
Which outputs:
Size: 6
8
2
3
4
1
5
Size: 4
2
3
4
1
Size: 0
Ofcourse remove, removeAll, retainAll now has the same or worse performance as ArrayList. But I do not use them and so I am ok with that.
Enjoy!
EDIT:
Here is another implementation, which does not extend LinkedHashSet because that's redundant. Instead it uses a HashSet and an ArrayList.
/**
* #Author Mo. Joseph
*
* Allows you to call get with o(1) instead of o(n) to get an instance by index
*/
public static final class $IndexLinkedHashSet<E> implements Set<E> {
private final ArrayList<E> list = new ArrayList<>( );
private final HashSet<E> set = new HashSet<> ( );
public synchronized boolean add(E e) {
if ( set.add(e) ) {
return list.add(e);
}
return false;
}
public synchronized boolean remove(Object o) {
if ( set.remove(o) ) {
return list.remove(o);
}
return false;
}
#Override
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
public synchronized void clear() {
set.clear();
list.clear();
}
public synchronized E get(int index) {
return list.get(index);
}
public synchronized boolean removeAll(Collection<?> c) {
if ( set.removeAll(c) ) {
return list.removeAll(c);
}
return true;
}
public synchronized boolean retainAll(Collection<?> c) {
if ( set.retainAll(c) ) {
return list.retainAll(c);
}
return false;
}
public synchronized boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
#Override
public synchronized int size() {
return set.size();
}
#Override
public synchronized boolean isEmpty() {
return set.isEmpty();
}
#Override
public synchronized boolean contains(Object o) {
return set.contains(o);
}
#Override
public synchronized Iterator<E> iterator() {
return list.iterator();
}
#Override
public synchronized Object[] toArray() {
return list.toArray();
}
#Override
public synchronized <T> T[] toArray(T[] a) {
return list.toArray(a);
}
}
Now you have two implementations, I would prefer the second one.
Although not as efficient for the machine, this achieves it in one line:
int index = new ArrayList<String>(names).indexOf("John");
A better way there is not, only a single lined one (which makes use of the iterator, too but implicitly):
new ArrayList(names).get(0)
You can convert your Set to List then you can do any indexing operations.
Example: need to crop Set list to 5 items.
Set<String> listAsLinkedHashSet = new LinkedHashSet<>();
listAsLinkedHashSet.add("1");
listAsLinkedHashSet.add("2");
listAsLinkedHashSet.add("3");
listAsLinkedHashSet.add("4");
listAsLinkedHashSet.add("1");
listAsLinkedHashSet.add("2");
listAsLinkedHashSet.add("5");
listAsLinkedHashSet.add("7");
listAsLinkedHashSet.add("9");
listAsLinkedHashSet.add("8");
listAsLinkedHashSet.add("1");
listAsLinkedHashSet.add("10");
listAsLinkedHashSet.add("11");
List<String> listAsArrayList = new ArrayList<>(listAsLinkedHashSet);
//crop list to 5 elements
if (listAsArrayList.size() > 5) {
for (int i = 5; i < listAsArrayList.size(); i++) {
listAsArrayList.remove(i);
i--;
}
}
listAsLinkedHashSet.clear();
listAsLinkedHashSet.addAll(listAsArrayList);
I need a queue with a fixed size. When I add an element and the queue is full, it should automatically remove the oldest element.
Is there an existing implementation for this in Java?
Actually the LinkedHashMap does exactly what you want. You need to override the removeEldestEntry method.
Example for a queue with max 10 elements:
queue = new LinkedHashMap<Integer, String>()
{
#Override
protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
{
return this.size() > 10;
}
};
If the "removeEldestEntry" returns true, the eldest entry is removed from the map.
Yes, Two
From my own duplicate question with this correct answer, I learned of two:
EvictingQueue in Google Guava
CircularFifoQueue in Apache Commons
I made productive use of the Guava EvictingQueue, worked well.
To instantiate an EvictingQueue call the static factory method create and specify your maximum size.
EvictingQueue< Person > people = com.google.common.collect.EvictingQueue.create( 100 ) ; // Set maximum size to 100.
I just implemented a fixed size queue this way:
public class LimitedSizeQueue<K> extends ArrayList<K> {
private int maxSize;
public LimitedSizeQueue(int size){
this.maxSize = size;
}
public boolean add(K k){
boolean r = super.add(k);
if (size() > maxSize){
removeRange(0, size() - maxSize);
}
return r;
}
public K getYoungest() {
return get(size() - 1);
}
public K getOldest() {
return get(0);
}
}
There is no existing implementation in the Java Language and Runtime. All Queues extend AbstractQueue, and its doc clearly states that adding an element to a full queue always ends with an exception. It would be best ( and quite simple ) to wrap a Queue into a class of your own for having the functionality you need.
Once again, because all queues are children of AbstractQueue, simply use that as your internal data type and you should have a flexible implementation running in virtually no time :-)
UPDATE:
As outlined below, there are two open implementations available (this answer is quite old, folks!), see this answer for details.
This is what I did with Queue wrapped with LinkedList, It is fixed sized which I give in here is 2;
public static Queue<String> pageQueue;
pageQueue = new LinkedList<String>(){
private static final long serialVersionUID = -6707803882461262867L;
public boolean add(String object) {
boolean result;
if(this.size() < 2)
result = super.add(object);
else
{
super.removeFirst();
result = super.add(object);
}
return result;
}
};
....
TMarket.pageQueue.add("ScreenOne");
....
TMarket.pageQueue.add("ScreenTwo");
.....
public class CircularQueue<E> extends LinkedList<E> {
private final int capacity;
public CircularQueue(int capacity){
this.capacity = capacity;
}
#Override
public boolean add(E e) {
if(size() >= capacity)
removeFirst();
return super.add(e);
}
}
Usage and test result:
public static void main(String[] args) {
CircularQueue<String> queue = new CircularQueue<>(3);
queue.add("a");
queue.add("b");
queue.add("c");
System.out.println(queue.toString()); //[a, b, c]
String first = queue.pollFirst(); //a
System.out.println(queue.toString()); //[b,c]
queue.add("d");
queue.add("e");
queue.add("f");
System.out.println(queue.toString()); //[d, e, f]
}
I think what you're describing is a circular queue. Here is an example and here is a better one
This class does the job using composition instead of inheritance (other answers here) which removes the possibility of certain side-effects (as covered by Josh Bloch in Essential Java). Trimming of the underlying LinkedList occurs on the methods add,addAll and offer.
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
public class LimitedQueue<T> implements Queue<T>, Iterable<T> {
private final int limit;
private final LinkedList<T> list = new LinkedList<T>();
public LimitedQueue(int limit) {
this.limit = limit;
}
private boolean trim() {
boolean changed = list.size() > limit;
while (list.size() > limit) {
list.remove();
}
return changed;
}
#Override
public boolean add(T o) {
boolean changed = list.add(o);
boolean trimmed = trim();
return changed || trimmed;
}
#Override
public int size() {
return list.size();
}
#Override
public boolean isEmpty() {
return list.isEmpty();
}
#Override
public boolean contains(Object o) {
return list.contains(o);
}
#Override
public Iterator<T> iterator() {
return list.iterator();
}
#Override
public Object[] toArray() {
return list.toArray();
}
#Override
public <T> T[] toArray(T[] a) {
return list.toArray(a);
}
#Override
public boolean remove(Object o) {
return list.remove(o);
}
#Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
#Override
public boolean addAll(Collection<? extends T> c) {
boolean changed = list.addAll(c);
boolean trimmed = trim();
return changed || trimmed;
}
#Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
#Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
#Override
public void clear() {
list.clear();
}
#Override
public boolean offer(T e) {
boolean changed = list.offer(e);
boolean trimmed = trim();
return changed || trimmed;
}
#Override
public T remove() {
return list.remove();
}
#Override
public T poll() {
return list.poll();
}
#Override
public T element() {
return list.element();
}
#Override
public T peek() {
return list.peek();
}
}
Sounds like an ordinary List where the add method contains an extra snippet which truncates the list if it gets too long.
If that is too simple, then you probably need to edit your problem description.
Also see this SO question, or ArrayBlockingQueue (be careful about blocking, this might be unwanted in your case).
It is not quite clear what requirements you have that led you to ask this question. If you need a fixed size data structure, you might also want to look at different caching policies. However, since you have a queue, my best guess is that you're looking for some type of router functionality. In that case, I would go with a ring buffer: an array that has a first and last index. Whenever an element is added, you just increment the last element index, and when an element is removed, increment the first element index. In both cases, addition is performed modulo the array size, and make sure to increment the other index when needed, that is, when the queue is full or empty.
Also, if it is a router-type application, you might also want to experiment with an algorithm such as Random Early Dropping (RED), which drops elements from the queue randomly even before it gets filled up. In some cases, RED has been found to have better overall performance than the simple method of allowing the queue to fill up before dropping.
Actually you can write your own impl based on LinkedList, it is quite straight forward, just override the add method and do the staff.
I think the best matching answer is from this other question.
Apache commons collections 4 has a CircularFifoQueue which is what you are looking for. Quoting the javadoc:
CircularFifoQueue is a first-in first-out queue with a fixed size that replaces its oldest element if full.
A Simple solution, below is a Queue of "String"
LinkedHashMap<Integer, String> queue;
int queueKeysCounter;
queue.put(queueKeysCounter++, "My String");
queueKeysCounter %= QUEUE_SIZE;
Note that this will not maintain the Order of the items in the Queue, but it will replace the oldest entry.
As it's advised in OOPs that we should prefer Composition over Inheritance
Here my solution keeping that in mind.
package com.choiceview;
import java.util.ArrayDeque;
class Ideone {
public static void main(String[] args) {
LimitedArrayDeque<Integer> q = new LimitedArrayDeque<>(3);
q.add(1);
q.add(2);
q.add(3);
System.out.println(q);
q.add(4);
// First entry ie 1 got pushed out
System.out.println(q);
}
}
class LimitedArrayDeque<T> {
private int maxSize;
private ArrayDeque<T> queue;
private LimitedArrayDeque() {
}
public LimitedArrayDeque(int maxSize) {
this.maxSize = maxSize;
queue = new ArrayDeque<T>(maxSize);
}
public void add(T t) {
if (queue.size() == maxSize) {
queue.removeFirst();
}
queue.add(t);
}
public boolean remove(T t) {
return queue.remove(t);
}
public boolean contains(T t) {
return queue.contains(t);
}
#Override
public String toString() {
return queue.toString();
}
}
Ok, I'll throw out my version too. :-) This is build to be very performant - for when that matters. It's not based on LinkedList - and is thread safe (should be at least). FIFO
static class FixedSizeCircularReference<T> {
T[] entries
FixedSizeCircularReference(int size) {
this.entries = new Object[size] as T[]
this.size = size
}
int cur = 0
int size
synchronized void add(T entry) {
entries[cur++] = entry
if (cur >= size) {
cur = 0
}
}
List<T> asList() {
int c = cur
int s = size
T[] e = entries.collect() as T[]
List<T> list = new ArrayList<>()
int oldest = (c == s - 1) ? 0 : c
for (int i = 0; i < e.length; i++) {
def entry = e[oldest + i < s ? oldest + i : oldest + i - s]
if (entry) list.add(entry)
}
return list
}
}