created a WeakPriorityBlockingQueue as special implementation of a PriorityBlockingQueue with a weak object binding. - better abstraction of ordering technique - fixed some bugs according to result numbering (distinguish different counters in Queue) - fixed a ordering bug in post-ranking (ordering was decreased instead of increased) - reversed ordering numbering using a reversed ordering. The higher the ranking number the better (now). git-svn-id: https://svn.berlios.de/svnroot/repos/yacy/trunk@7128 6c8d7289-2bf4-0310-a012-ef5d649a1542pull/1/head
parent
03eb021568
commit
348dece62f
@ -0,0 +1,352 @@
|
||||
/**
|
||||
* WeakPriorityBlockingQueue
|
||||
* an priority blocking queue that drains elements if it gets too large
|
||||
* Copyright 2010 by Michael Peter Christen, mc@yacy.net, Frankfurt a. M., Germany
|
||||
* First released 09.09.2010 at http://yacy.net
|
||||
*
|
||||
* This library 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; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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 this program in the file lgpl21.txt
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.yacy.cora.storage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* implements a stack where elements 'float' on-top of the stack according to a weight value.
|
||||
* objects pushed on the stack must implement the hashCode() method to provide a handle
|
||||
* for a double-check.
|
||||
* If the queue gets larger that the given maxsize, then elements from the tail of the queue
|
||||
* are drained (deleted).
|
||||
*/
|
||||
public class WeakPriorityBlockingQueue<E> {
|
||||
|
||||
|
||||
private final TreeSet<E> queue; // object within the stack, ordered using a TreeSet
|
||||
private final Semaphore enqueued; // semaphore for elements in the stack
|
||||
private final ArrayList<E> drained; // objects that had been on the stack but had been removed
|
||||
protected int maxsize;
|
||||
|
||||
/**
|
||||
* create a new WeakPriorityBlockingQueue
|
||||
* all elements in the stack are not ordered by their insert order but by a given element weight
|
||||
* weights that are preferred are returned first when a pop from the stack is made
|
||||
* @param maxsize the maximum size of the stack. When the stack exceeds this number, then entries are removed
|
||||
*/
|
||||
public WeakPriorityBlockingQueue(final int maxsize) {
|
||||
// the maxsize is the maximum number of entries in the stack
|
||||
// if this is set to -1, the size is unlimited
|
||||
this.queue = new TreeSet<E>();
|
||||
this.drained = new ArrayList<E>();
|
||||
this.enqueued = new Semaphore(0);
|
||||
this.maxsize = maxsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* clear the queue
|
||||
*/
|
||||
public synchronized void clear() {
|
||||
this.drained.clear();
|
||||
this.queue.clear();
|
||||
this.enqueued.drainPermits();
|
||||
}
|
||||
|
||||
/**
|
||||
* test if the queue is empty
|
||||
* @return true if the queue is empty, false if not
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.queue.isEmpty() & this.drained.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of elements in the queue, waiting to be removed with take() or poll()
|
||||
* @return
|
||||
*/
|
||||
public synchronized int sizeQueue() {
|
||||
return this.queue.size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* get the number of elements that had been drained so far and are wainting
|
||||
* in a list to get enumerated with element()
|
||||
* @return
|
||||
*/
|
||||
public synchronized int sizeDrained() {
|
||||
return this.drained.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* get the number of elements that are available for retrieval
|
||||
* this is a combined number of sizeQueue() and sizeDrained();
|
||||
* @return
|
||||
*/
|
||||
public synchronized int sizeAvailable() {
|
||||
return this.queue.size() + this.drained.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* put a element on the stack using a order of the weight
|
||||
* elements that had been on the stack cannot be put in again,
|
||||
* they are checked against the drained list
|
||||
* @param element the element (must have a equals() method)
|
||||
* @param weight the weight of the element
|
||||
* @param remove - the rating of the element that shall be removed in case that the stack has an size overflow
|
||||
*/
|
||||
public synchronized void put(final E element) {
|
||||
// put the element on the stack
|
||||
if (this.drained.contains(element)) return;
|
||||
if (this.queue.size() == this.maxsize) {
|
||||
// remove last elements if stack is too large
|
||||
this.queue.remove(this.queue.last());
|
||||
this.queue.add(element);
|
||||
} else {
|
||||
this.queue.add(element);
|
||||
this.enqueued.release();
|
||||
}
|
||||
assert this.queue.size() == this.enqueued.availablePermits();
|
||||
}
|
||||
|
||||
/**
|
||||
* return the element with the smallest weight and remove it from the stack
|
||||
* @return null if no element is on the queue or the head of the queue
|
||||
*/
|
||||
public synchronized E poll() {
|
||||
if (this.queue.isEmpty()) return null;
|
||||
this.enqueued.tryAcquire();
|
||||
return takeUnsafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and removes the head of this queue, waiting if necessary
|
||||
* up to the specified wait time if no elements are present on this queue.
|
||||
* @param timeout milliseconds until timeout
|
||||
* @return the head element from the queue
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public synchronized E poll(long timeout) throws InterruptedException {
|
||||
boolean a = this.enqueued.tryAcquire(timeout, TimeUnit.MILLISECONDS);
|
||||
if (!a) return null;
|
||||
return takeUnsafe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves and removes the head of this queue, waiting if no elements are present on this queue.
|
||||
* @return the head element from the queue
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public synchronized E take() throws InterruptedException {
|
||||
this.enqueued.acquire();
|
||||
return takeUnsafe();
|
||||
}
|
||||
|
||||
private E takeUnsafe() {
|
||||
final E element = this.queue.first();
|
||||
assert element != null;
|
||||
this.queue.remove(element);
|
||||
this.drained.add(element);
|
||||
assert this.queue.size() == this.enqueued.availablePermits();
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* return the element with the smallest weight, but do not remove it
|
||||
* @return null if no element is on the queue or the head of the queue
|
||||
*/
|
||||
public synchronized E peek() {
|
||||
if (this.queue.isEmpty()) return null;
|
||||
return this.queue.first();
|
||||
}
|
||||
|
||||
/**
|
||||
* all objects that have been returned by poll or take are stored in a back-up list
|
||||
* where they can be retrieved afterward. The elements from that list are stored in
|
||||
* the specific order as they had been retrieved. This method returns the elements
|
||||
* in that specific order and if the list is not large enough, elements available
|
||||
* with poll() are taken and written to the list until the required position is
|
||||
* written. If the stach size together with the recorded list is not large enough,
|
||||
* null is returned
|
||||
* @param position inside the drained queue
|
||||
* @return the element from the recorded position or null if that position is not available
|
||||
*/
|
||||
public synchronized E element(final int position) {
|
||||
if (position < this.drained.size()) {
|
||||
return this.drained.get(position);
|
||||
}
|
||||
if (position >= this.queue.size() + this.drained.size()) return null; // we don't have that element
|
||||
while (position >= this.drained.size()) this.poll();
|
||||
return this.drained.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* retrieve an element from the drained queue but wait until a timeout
|
||||
* until returning null when no element will be available within the time
|
||||
* from the input queue
|
||||
* @param position inside the drained queue
|
||||
* @param time the timeout
|
||||
* @return the element from the recorded position or null if that position is not available within the timeout
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public synchronized E element(final int position, long time) throws InterruptedException {
|
||||
long timeout = System.currentTimeMillis() + time;
|
||||
if (position < this.drained.size()) {
|
||||
return this.drained.get(position);
|
||||
}
|
||||
if (position >= this.queue.size() + this.drained.size()) return null; // we don't have that element
|
||||
while (position >= this.drained.size()) {
|
||||
long t = timeout - System.currentTimeMillis();
|
||||
if (t <= 0) break;
|
||||
this.poll(t);
|
||||
}
|
||||
if (position >= this.drained.size()) return null; // we still don't have that element
|
||||
return this.drained.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the specific amount of entrie as they would be retrievable with element()
|
||||
* if count is < 0 then all elements are taken
|
||||
* the returned list is not cloned from the internal list and shall not be modified in any way (read-only)
|
||||
* @param count
|
||||
* @return a list of elements in the stack
|
||||
*/
|
||||
public synchronized ArrayList<E> list(final int count) {
|
||||
if (count < 0) {
|
||||
// shift all elements
|
||||
while (!this.queue.isEmpty()) this.poll();
|
||||
return this.drained;
|
||||
}
|
||||
if (count > sizeAvailable()) throw new RuntimeException("list(" + count + ") exceeded avaiable number of elements (" + sizeAvailable() + ")");
|
||||
while (count > this.drained.size()) this.poll();
|
||||
return this.drained;
|
||||
}
|
||||
|
||||
/**
|
||||
* iterate over all elements available. All elements that are still in the queue are drained to recorded positions
|
||||
* @return an iterator over all drained positions.
|
||||
*/
|
||||
public synchronized Iterator<E> iterator() {
|
||||
// shift all elements to the offstack
|
||||
while (!this.queue.isEmpty()) this.poll();
|
||||
return this.drained.iterator();
|
||||
}
|
||||
|
||||
protected interface Element<E> {
|
||||
public long getWeight();
|
||||
public E getElement();
|
||||
public boolean equals(Element<E> o);
|
||||
public int hashCode();
|
||||
public String toString();
|
||||
}
|
||||
|
||||
protected abstract static class AbstractElement<E> {
|
||||
|
||||
public long weight;
|
||||
public E element;
|
||||
|
||||
public long getWeight() {
|
||||
return this.weight;
|
||||
}
|
||||
|
||||
public E getElement() {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
public boolean equals(Element<E> o) {
|
||||
return this.element.equals(o.getElement());
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.element.hashCode();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return element.toString() + "/" + weight;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* natural ordering elements, can be used as container of objects <E> in the priority queue
|
||||
* the elements with smallest ordering weights are first in the queue when elements are taken
|
||||
*/
|
||||
public static class NaturalElement<E> extends AbstractElement<E> implements Comparable<NaturalElement<E>>, Comparator<NaturalElement<E>> {
|
||||
|
||||
public NaturalElement(final E element, final long weight) {
|
||||
this.element = element;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
public int compare(NaturalElement<E> o1, NaturalElement<E> o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
|
||||
public int compareTo(NaturalElement<E> o) {
|
||||
if (this.element == o.getElement()) return 0;
|
||||
if (this.element.equals(o.getElement())) return 0;
|
||||
if (this.weight > o.getWeight()) return 1;
|
||||
if (this.weight < o.getWeight()) return -1;
|
||||
int o1h = this.hashCode();
|
||||
int o2h = o.hashCode();
|
||||
if (o1h > o2h) return 1;
|
||||
if (o1h < o2h) return -1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reverse ordering elements, can be used as container of objects <E> in the priority queue
|
||||
* the elements with highest ordering weights are first in the queue when elements are taken
|
||||
*/
|
||||
public static class ReverseElement<E> extends AbstractElement<E> implements Comparable<ReverseElement<E>>, Comparator<ReverseElement<E>> {
|
||||
|
||||
public ReverseElement(final E element, final long weight) {
|
||||
this.element = element;
|
||||
this.weight = weight;
|
||||
}
|
||||
|
||||
public int compare(ReverseElement<E> o1, ReverseElement<E> o2) {
|
||||
return o1.compareTo(o2);
|
||||
}
|
||||
|
||||
public int compareTo(ReverseElement<E> o) {
|
||||
if (this.element == o.getElement()) return 0;
|
||||
if (this.element.equals(o.getElement())) return 0;
|
||||
if (this.weight > o.getWeight()) return -1;
|
||||
if (this.weight < o.getWeight()) return 1;
|
||||
int o1h = this.hashCode();
|
||||
int o2h = o.hashCode();
|
||||
if (o1h > o2h) return -1;
|
||||
if (o1h < o2h) return 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
WeakPriorityBlockingQueue<ReverseElement<String>> a = new WeakPriorityBlockingQueue<ReverseElement<String>>(3);
|
||||
a.put(new ReverseElement<String>("abc", 1));
|
||||
//a.poll();
|
||||
a.put(new ReverseElement<String>("abcx", 2));
|
||||
a.put(new ReverseElement<String>("6s_7dfZk4xvc", 3));
|
||||
a.put(new ReverseElement<String>("6s_7dfZk4xvcx", 4));
|
||||
//a.poll();
|
||||
System.out.println("size = " + a.sizeAvailable());
|
||||
while (a.sizeQueue() > 0) System.out.println("> " + a.poll().toString());
|
||||
}
|
||||
}
|
@ -1,205 +0,0 @@
|
||||
// kelondroSortStack.java
|
||||
// (C) 2008 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
|
||||
// first published 20.02.2008 on http://yacy.net
|
||||
//
|
||||
// This is a part of YaCy, a peer-to-peer based web search engine
|
||||
//
|
||||
// $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $
|
||||
// $LastChangedRevision: 1986 $
|
||||
// $LastChangedBy: orbiter $
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package net.yacy.kelondro.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class SortStack<E> {
|
||||
|
||||
// implements a stack where elements 'float' on-top of the stack according to a weight value.
|
||||
// objects pushed on the stack must implement the hashCode() method to provide a handle
|
||||
// for a double-check.
|
||||
|
||||
private static final Object PRESENT = new Object(); // Dummy value to associate with an Object in the backing Map
|
||||
private TreeMap<Long, List<E>> onstack; // object within the stack
|
||||
private ConcurrentHashMap<E, Object> instack; // keeps track which element has been on the stack
|
||||
protected int maxsize;
|
||||
private boolean upward;
|
||||
|
||||
public SortStack(boolean upward) {
|
||||
this(-1, upward);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new sort stack
|
||||
* all elements in the stack are not ordered by their insert order but by a given element weight
|
||||
* weights that are preferred are returned first when a pop from the stack is made
|
||||
* the stack may be ordered upward (preferring small weights) or downward (preferring high wights)
|
||||
* @param maxsize the maximum size of the stack. When the stack exceeds this number, then the worst entries according to entry order are removed
|
||||
* @param upward is the entry order and controls which elements are returned on pop. if true, then the smallest is returned first
|
||||
*/
|
||||
public SortStack(final int maxsize, boolean upward) {
|
||||
// the maxsize is the maximum number of entries in the stack
|
||||
// if this is set to -1, the size is unlimited
|
||||
this.onstack = new TreeMap<Long, List<E>>();
|
||||
this.instack = new ConcurrentHashMap<E, Object>();
|
||||
this.maxsize = maxsize;
|
||||
this.upward = upward;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.instack.isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
/*
|
||||
int c = 0;
|
||||
synchronized (onstack) {
|
||||
for (List<E> l: onstack.values()) c += l.size();
|
||||
assert c == this.instack.size() : "c = " + c + "; this.size() = " + this.instack.size();
|
||||
}
|
||||
*/
|
||||
return this.instack.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* put a element on the stack using a order of the weight
|
||||
* @param element
|
||||
* @param weight
|
||||
*/
|
||||
public void push(final E element, Long weight) {
|
||||
// put the element on the stack
|
||||
synchronized (this.onstack) {
|
||||
if (this.instack.put(element, PRESENT) != null) return;
|
||||
|
||||
List<E> l = this.onstack.get(weight);
|
||||
if (l == null) {
|
||||
l = new LinkedList<E>();
|
||||
l.add(element);
|
||||
this.onstack.put(weight, l);
|
||||
} else {
|
||||
l.add(element);
|
||||
}
|
||||
//this.instack.put(element, PRESENT);
|
||||
}
|
||||
// check maximum size of the stack an remove elements if the stack gets too large
|
||||
if (this.maxsize <= 0) return;
|
||||
while (!this.onstack.isEmpty() && this.onstack.size() > this.maxsize) synchronized (this.onstack) {
|
||||
List<E> l;
|
||||
if (!this.onstack.isEmpty() && this.onstack.size() > this.maxsize) {
|
||||
l = this.onstack.remove((this.upward) ? this.onstack.lastKey() : this.onstack.firstKey());
|
||||
for (E e: l) instack.remove(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the element with the smallest weight
|
||||
* @return
|
||||
*/
|
||||
public stackElement top() {
|
||||
// returns the element that is currently on top of the stack
|
||||
final E element;
|
||||
final Long w;
|
||||
synchronized (this.onstack) {
|
||||
if (this.onstack.isEmpty()) return null;
|
||||
w = (this.upward) ? this.onstack.firstKey() : this.onstack.lastKey();
|
||||
final List<E> l = this.onstack.get(w);
|
||||
element = l.get(0);
|
||||
}
|
||||
return new stackElement(element, w);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the element with the smallest weight and remove it from the stack
|
||||
* @return
|
||||
*/
|
||||
public stackElement pop() {
|
||||
// returns the element that is currently on top of the stack
|
||||
// it is removed and added to the offstack list
|
||||
final E element;
|
||||
final Long w;
|
||||
synchronized (this.onstack) {
|
||||
if (this.onstack.isEmpty()) return null;
|
||||
w = (this.upward) ? this.onstack.firstKey() : this.onstack.lastKey();
|
||||
final List<E> l = this.onstack.get(w);
|
||||
element = l.remove(0);
|
||||
this.instack.remove(element);
|
||||
if (l.isEmpty()) this.onstack.remove(w);
|
||||
}
|
||||
return new stackElement(element, w);
|
||||
}
|
||||
|
||||
public boolean exists(final E element) {
|
||||
// uses the hashCode of the element to find out of the element had been on the list or the stack
|
||||
return this.instack.contains(element);
|
||||
}
|
||||
|
||||
public void remove(final E element) {
|
||||
synchronized (this.onstack) {
|
||||
if (!this.instack.contains(element)) return;
|
||||
for (Map.Entry<Long,List<E>> entry: this.onstack.entrySet()) {
|
||||
Iterator<E> i = entry.getValue().iterator();
|
||||
while (i.hasNext()) {
|
||||
if (i.next().equals(element)) {
|
||||
i.remove();
|
||||
if (entry.getValue().isEmpty()) {
|
||||
this.onstack.remove(entry.getKey());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean bottom(final long weight) {
|
||||
// returns true if the element with that weight would be on the bottom of the stack after inserting
|
||||
if (this.onstack.isEmpty()) return true;
|
||||
Long l;
|
||||
|
||||
if (this.upward) {
|
||||
synchronized (this.onstack) {
|
||||
l = this.onstack.lastKey();
|
||||
}
|
||||
return weight > l.longValue();
|
||||
} else {
|
||||
synchronized (this.onstack) {
|
||||
l = this.onstack.firstKey();
|
||||
}
|
||||
return weight < l.longValue();
|
||||
}
|
||||
}
|
||||
|
||||
public class stackElement {
|
||||
public Long weight;
|
||||
public E element;
|
||||
public stackElement(final E element, final Long weight) {
|
||||
this.element = element;
|
||||
this.weight = weight;
|
||||
}
|
||||
public String toString() {
|
||||
return element.toString() + "/" + weight;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
// kelondroSortStore.java
|
||||
// (C) 2008 by Michael Peter Christen; mc@yacy.net, Frankfurt a. M., Germany
|
||||
// first published 20.02.2008 on http://yacy.net
|
||||
//
|
||||
// This is a part of YaCy, a peer-to-peer based web search engine
|
||||
//
|
||||
// $LastChangedDate: 2006-04-02 22:40:07 +0200 (So, 02 Apr 2006) $
|
||||
// $LastChangedRevision: 1986 $
|
||||
// $LastChangedBy: orbiter $
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package net.yacy.kelondro.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* extends the sortStack in such a way that it adds a list where objects, that had
|
||||
* been pulled from the stack with pop are listed. Provides access methods to address
|
||||
* specific elements in the list.
|
||||
* @param <E>
|
||||
*/
|
||||
public class SortStore<E> extends SortStack<E> {
|
||||
|
||||
private static final Object PRESENT = new Object(); // Dummy value to associate with an Object in the backing Map
|
||||
private final ArrayList<stackElement> offstack; // objects that had been on the stack but had been removed
|
||||
private ConcurrentHashMap<E, Object> offset; // keeps track which element has been on the stack or is now in the offstack
|
||||
private long largest;
|
||||
|
||||
public SortStore(boolean upward) {
|
||||
this(-1, upward);
|
||||
}
|
||||
|
||||
/**
|
||||
* create a new sort stack
|
||||
* all elements in the stack are not ordered by their insert order but by a given element weight
|
||||
* weights that are preferred are returned first when a pop from the stack is made
|
||||
* the stack may be ordered upward (preferring small weights) or downward (preferring high wights)
|
||||
* @param maxsize the maximum size of the stack. When the stack exceeds this number, then the worst entries according to entry order are removed
|
||||
* @param upward is the entry order and controls which elements are returned on pop. if true, then the smallest is returned first
|
||||
*/
|
||||
public SortStore(final int maxsize, boolean upward) {
|
||||
super(maxsize, upward);
|
||||
this.largest = Long.MIN_VALUE;
|
||||
this.offstack = new ArrayList<stackElement>();
|
||||
this.offset = new ConcurrentHashMap<E, Object>();
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
if (!super.isEmpty()) return false;
|
||||
return this.offstack.isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return super.size() + this.offstack.size();
|
||||
}
|
||||
|
||||
public void push(final E element, final Long weight) {
|
||||
if (this.offset.containsKey(element)) return;
|
||||
if (super.exists(element)) return;
|
||||
super.push(element, weight);
|
||||
this.largest = Math.max(this.largest, weight.longValue());
|
||||
if (this.maxsize <= 0) return;
|
||||
while (!super.isEmpty() && this.size() > this.maxsize) {
|
||||
this.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the element that is currently on top of the stack
|
||||
* it is removed and added to the offstack list
|
||||
* this is exactly the same as element(offstack.size())
|
||||
*/
|
||||
public stackElement pop() {
|
||||
final stackElement se = super.pop();
|
||||
if (se == null) return null;
|
||||
this.offset.put(se.element, PRESENT);
|
||||
this.offstack.add(se);
|
||||
return se;
|
||||
}
|
||||
|
||||
public stackElement top() {
|
||||
return super.top();
|
||||
}
|
||||
|
||||
public boolean exists(final E element) {
|
||||
return super.exists(element) || this.offset.containsKey(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* return an element from a specific position. It is either taken from the offstack,
|
||||
* or removed from the onstack.
|
||||
* The offstack will grow if elements are not from the offstack and present at the onstack.
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
public stackElement element(final int position) {
|
||||
if (position < this.offstack.size()) {
|
||||
return this.offstack.get(position);
|
||||
}
|
||||
if (position >= super.size() + this.offstack.size()) return null; // we don't have that element
|
||||
while (position >= this.offstack.size()) this.pop();
|
||||
return this.offstack.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* return the specific amount of entries. If they are not yet present in the offstack, they are shifted there from the onstack
|
||||
* if count is < 0 then all elements are taken
|
||||
* the returned list is not cloned from the internal list and shall not be modified in any way (read-only)
|
||||
* @param count
|
||||
* @return
|
||||
*/
|
||||
public ArrayList<stackElement> list(final int count) {
|
||||
if (count < 0) {
|
||||
// shift all elements
|
||||
while (!super.isEmpty()) this.pop();
|
||||
return this.offstack;
|
||||
}
|
||||
if (count > super.size() + this.offstack.size()) throw new RuntimeException("list(" + count + ") exceeded avaiable number of elements (" + size() + ")");
|
||||
while (count > this.offstack.size()) this.pop();
|
||||
return this.offstack;
|
||||
}
|
||||
|
||||
public void remove(final E element) {
|
||||
super.remove(element);
|
||||
synchronized (this.offstack) {
|
||||
Iterator<stackElement> i = this.offstack.iterator();
|
||||
while (i.hasNext()) {
|
||||
if (i.next().element.equals(element)) {
|
||||
i.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean bottom(final long weight) {
|
||||
if (super.bottom(weight)) return true;
|
||||
return weight > this.largest;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SortStore<String> a = new SortStore<String>(true);
|
||||
a.push("abc", 1L);
|
||||
a.pop();
|
||||
a.push("abc", 2L);
|
||||
a.push("6s_7dfZk4xvc", 1L);
|
||||
a.push("6s_7dfZk4xvc", 1L);
|
||||
a.pop();
|
||||
System.out.println("size = " + a.size());
|
||||
}
|
||||
}
|
Loading…
Reference in new issue