%PDF- %PDF-
| Direktori : /home/waritko/yacy/source/net/yacy/cora/sorting/ |
| Current File : //home/waritko/yacy/source/net/yacy/cora/sorting/ClusteredScoreMap.java |
/**
* ScoreCluster
* Copyright 2004, 2010 by Michael Peter Christen, mc@yacy.net, Frankfurt am Main, Germany
* First released 28.09.2004 at http://yacy.net
*
* $LastChangedDate$
* $LastChangedRevision$
* $LastChangedBy$
*
* 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.sorting;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
import net.yacy.cora.storage.OutOfLimitsException;
public final class ClusteredScoreMap<E> extends AbstractScoreMap<E> implements ReversibleScoreMap<E> {
private final Map<E, Long> map; // a mapping from a reference to the cluster key
private final TreeMap<Long, E> pam; // a mapping from the cluster key to the reference
private long gcount;
private int encnt;
/**
* create a sorted map where there is a choice between a hash map or a tree map for the key store
* @param sortedKeys if true, a tree map is used for key storage; in this case the iterator() returns a sorted list of keys; if sortedKey is set to false, a linked hash map is used which preserves the original key appearance order
*/
public ClusteredScoreMap(boolean sortedKeys) {
this.map = sortedKeys ? new TreeMap<E, Long>() : new LinkedHashMap<E, Long>();
this.pam = new TreeMap<Long, E>();
this.gcount = 0;
this.encnt = 0;
}
public ClusteredScoreMap(final Comparator<E> c) {
this.map = new TreeMap<E, Long>(c);
this.pam = new TreeMap<Long, E>();
this.gcount = 0;
this.encnt = 0;
}
@Override
public Iterator<E> iterator() {
return this.map.keySet().iterator();
}
@Override
public synchronized void clear() {
this.map.clear();
this.pam.clear();
this.gcount = 0;
this.encnt = 0;
}
@Override
public int shrinkToMaxSize(final int maxsize) {
if (maxsize < 0) {
return 0;
}
Long key;
int deletedNb = 0;
synchronized (this) {
while (this.map.size() > maxsize) {
// find and remove smallest objects until cluster has demanded size
key = this.pam.firstKey();
if (key == null) break;
this.map.remove(this.pam.remove(key));
deletedNb++;
}
}
return deletedNb;
}
@Override
public int shrinkToMinScore(final int minScore) {
int score;
Long key;
int deletedNb = 0;
synchronized (this) {
while (!this.pam.isEmpty()) {
// find and remove objects where their score is smaller than the demanded minimum score
key = this.pam.firstKey();
if (key == null) break;
score = (int) ((key.longValue() & 0xFFFFFFFF00000000L) >> 32);
if (score >= minScore) break;
this.map.remove(this.pam.remove(key));
deletedNb++;
}
}
return deletedNb;
}
private long scoreKey(final int elementNr, final int elementCount) {
return (((elementCount & 0xFFFFFFFFL)) << 32) | ((elementNr & 0xFFFFFFFFL));
}
private synchronized long totalCount() {
return this.gcount;
}
@Override
public synchronized int size() {
return this.map.size();
}
@Override
public boolean sizeSmaller(final int size) {
return this.map.size() < size;
}
@Override
public synchronized boolean isEmpty() {
return this.map.isEmpty();
}
@Override
public synchronized void inc(final E obj) {
inc(obj, 1);
}
@Override
public synchronized void dec(final E obj) {
inc(obj, -1);
}
@Override
public void set(final E obj, final int newScore) {
if (obj == null) return;
synchronized (this) {
Long usk = this.map.remove(obj); // get unique score key, old entry is not needed any more
if (newScore < 0) throw new OutOfLimitsException(newScore);
if (usk == null) {
// set new value
usk = Long.valueOf(scoreKey(this.encnt++, newScore));
// put new value into cluster
this.map.put(obj, usk);
this.pam.put(usk, obj);
} else {
// delete old entry
this.pam.remove(usk);
// get previous handle and score
final long c = usk.longValue();
final int oldScore = (int) ((c & 0xFFFFFFFF00000000L) >> 32);
final int oldHandle = (int) (c & 0xFFFFFFFFL);
this.gcount -= oldScore;
// set new value
usk = Long.valueOf(scoreKey(oldHandle, newScore)); // generates an unique key for a specific score
this.map.put(obj, usk);
this.pam.put(usk, obj);
}
}
// increase overall counter
this.gcount += newScore;
}
@Override
public void inc(final E obj, final int incrementScore) {
if (obj == null) return;
synchronized (this) {
Long usk = this.map.remove(obj); // get unique score key, old entry is not needed any more
if (usk == null) {
// set new value
if (incrementScore < 0) throw new OutOfLimitsException(incrementScore);
usk = Long.valueOf(scoreKey(this.encnt++, incrementScore));
// put new value into cluster
this.map.put(obj, usk);
this.pam.put(usk, obj);
} else {
// delete old entry
this.pam.remove(usk);
// get previous handle and score
final long c = usk.longValue();
final int oldScore = (int) ((c & 0xFFFFFFFF00000000L) >> 32);
final int oldHandle = (int) (c & 0xFFFFFFFFL);
// set new value
final int newValue = oldScore + incrementScore;
if (newValue < 0) throw new OutOfLimitsException(newValue);
usk = Long.valueOf(scoreKey(oldHandle, newValue)); // generates an unique key for a specific score
this.map.put(obj, usk);
this.pam.put(usk, obj);
}
}
// increase overall counter
this.gcount += incrementScore;
}
@Override
public void dec(final E obj, final int incrementScore) {
inc(obj, -incrementScore);
}
@Override
public int delete(final E obj) {
// deletes entry and returns previous score
if (obj == null) return 0;
final Long usk;
synchronized (this) {
usk = this.map.remove(obj); // get unique score key, old entry is not needed any more
if (usk == null) return 0;
// delete old entry
this.pam.remove(usk);
}
// get previous handle and score
final int oldScore = (int) ((usk.longValue() & 0xFFFFFFFF00000000L) >> 32);
// decrease overall counter
this.gcount -= oldScore;
return oldScore;
}
@Override
public synchronized boolean containsKey(final E obj) {
return this.map.containsKey(obj);
}
@Override
public int get(final E obj) {
if (obj == null) return 0;
final Long cs;
synchronized (this) {
cs = this.map.get(obj);
}
if (cs == null) return 0;
return (int) ((cs.longValue() & 0xFFFFFFFF00000000L) >> 32);
}
@Override
public synchronized int getMaxScore() {
if (this.map.isEmpty()) return -1;
return (int) ((this.pam.lastKey().longValue() & 0xFFFFFFFF00000000L) >> 32);
}
@Override
public synchronized int getMinScore() {
if (this.map.isEmpty()) return -1;
return (int) ((this.pam.firstKey().longValue() & 0xFFFFFFFF00000000L) >> 32);
}
@Override
public synchronized E getMaxKey() {
if (this.map.isEmpty()) return null;
return this.pam.get(this.pam.lastKey());
}
@Override
public synchronized E getMinKey() {
if (this.map.isEmpty()) return null;
return this.pam.get(this.pam.firstKey());
}
@Override
public synchronized Iterator<E> keys(final boolean up) {
if (up) return new simpleScoreIterator<E>();
return new reverseScoreIterator<E>();
}
private class reverseScoreIterator<A extends E> implements Iterator<E> {
SortedMap<Long, E> view;
Long key;
public reverseScoreIterator() {
this.view = ClusteredScoreMap.this.pam;
}
@Override
public boolean hasNext() {
return !this.view.isEmpty();
}
@Override
public E next() {
this.key = this.view.lastKey();
this.view = this.view.headMap(this.key);
final E value = ClusteredScoreMap.this.pam.get(this.key);
//System.out.println("cluster reverse iterator: score = " + ((((Long) key).longValue() & 0xFFFFFFFF00000000L) >> 32) + ", handle = " + (((Long) key).longValue() & 0xFFFFFFFFL) + ", value = " + value);
return value;
}
@Override
public void remove() {
final Object val = ClusteredScoreMap.this.pam.remove(this.key);
if (val != null) ClusteredScoreMap.this.map.remove(val);
}
}
private class simpleScoreIterator<A extends E> implements Iterator<E> {
Iterator<Map.Entry<Long, E>> ii;
Map.Entry<Long, E> entry;
public simpleScoreIterator() {
this.ii = ClusteredScoreMap.this.pam.entrySet().iterator();
}
@Override
public boolean hasNext() {
return this.ii.hasNext();
}
@Override
public E next() {
this.entry = this.ii.next();
//System.out.println("cluster simple iterator: score = " + ((((Long) entry.getKey()).longValue() & 0xFFFFFFFF00000000L) >> 32) + ", handle = " + (((Long) entry.getKey()).longValue() & 0xFFFFFFFFL) + ", value = " + entry.getValue());
return this.entry.getValue();
}
@Override
public void remove() {
this.ii.remove();
if (this.entry.getValue() != null) ClusteredScoreMap.this.map.remove(this.entry.getValue());
}
}
public static void main(final String[] args) {
System.out.println("Test for Score: start");
final ClusteredScoreMap<String> s = new ClusteredScoreMap<String>(false);
long c = 0;
// create cluster
final long time = System.currentTimeMillis();
final Random random = new Random(1234);
int r;
final int count = 20;
for (int x = 0; x < 100000; x++) {
for (int i = 0; i < count; i++) {
r = Math.abs(random.nextInt(100));
s.inc("score#" + r, r);
c += r;
}
// delete some
int p;
for (int i = 0; i < (count / 2); i++) {
p = Math.abs(random.nextInt(1000));
if (s.containsKey("score#" + p)) {
//System.out.println("delete score#" + s.get("score#" + p));
c -= s.delete("score#" + p);
}
}
}
System.out.println("finished create. time = " + (System.currentTimeMillis() - time));
System.out.println("result:");
Iterator<String> i = s.keys(true);
while (i.hasNext()) System.out.println("up: " + i.next());
i = s.keys(false);
while (i.hasNext()) System.out.println("down: " + i.next());
System.out.println("total=" + s.totalCount() + ", elements=" + s.size() + ", redundant count=" + c);
}
}