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