All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.mmm.util.io.base.RankMap Maven / Gradle / Ivy

/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0 */
package net.sf.mmm.util.io.base;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * This class is like a {@link Map} that maps elements ({@literal }) to an {@code int} value that represents the
 * {@link #getRank(Object) rank} of the according element. 
* This is useful for heuristic decisions where specific detections indicate a {@link #addRank(Object, int) gain or * loss} of a specific element (representing a decision). Additionally an element can be {@link #setUnacceptable(Object) * declared} {@link #RANK_UNACCEPTABLE unacceptable} so it is out of the decision. * * @param is the type of the elements (decisions) to rank. * * @author Joerg Hohwiller (hohwille at users.sourceforge.net) * @since 1.0.1 */ @SuppressWarnings("javadoc") class RankMap { /** * The rank that represents an element that has been disqualified. If an element has this rank it can NOT change it * again and calls to {@link #addRank(Object, int)} will have no effect. * * @see #setUnacceptable(Object) */ public static final int RANK_UNACCEPTABLE = Integer.MIN_VALUE; private final Map map; private final Collection elements; /** * The constructor. */ public RankMap() { super(); this.map = new HashMap<>(); this.elements = Collections.unmodifiableCollection(this.map.keySet()); } /** * This method gets all elements in this {@link RankMap} that have been actively ranked (by * {@link #addRank(Object, int)} or {@link #setUnacceptable(Object)}). * * @return a {@link Collection} with the ranked elements. */ public Collection getElements() { return this.elements; } /** * This method gets the current rank for the given {@code element}. The rank is in the range from * {@link #RANK_UNACCEPTABLE -1} to {@link Integer#MAX_VALUE}. If the {@code element} has no rank, a value of * {@code 0} will be returned. * * @see #RANK_UNACCEPTABLE * @see #addRank(Object, int) * * @param element is the element for which the rank is requested. * @return the current rank of the given {@code element}. */ public int getRank(E element) { Ranking ranking = this.map.get(element); if (ranking == null) { return 0; } else { return ranking.rank; } } /** * This method ranks the given {@code element} as {@link #RANK_UNACCEPTABLE unacceptable}. After the call of this * method the {@link #getRank(Object) rank} of the given {@code element} is set to {@link #RANK_UNACCEPTABLE} and can * NOT be changed again. * * @param element is the unacceptable element. */ public void setUnacceptable(E element) { Ranking ranking = this.map.get(element); if (ranking == null) { ranking = new Ranking(); this.map.put(element, ranking); } ranking.setUnacceptable(); } /** * This method determines if the given {@code element} has been {@link #setUnacceptable(Object) declared} * {@link #RANK_UNACCEPTABLE unacceptable}. * * @param element is the element to check. * @return {@code true} if the given {@code element} is unacceptable, {@code false} if the given element is * acceptable. */ public boolean isUnacceptable(E element) { Ranking ranking = this.map.get(element); if (ranking == null) { return false; } return ranking.isUnacceptable(); } /** * This method adds the given {@code gain} to the current {@link #getRank(Object) rank} of the given {@code element}. * If the {@code element} is {@link #setUnacceptable(Object) unacceptable}, this method will have no effect. This * method guarantees that there will be no overflow of the {@link #getRank(Object) rank}. * * @param element is the element to rank. * @param gain is the value to add to the current rank. It may be negative to reduce the rank. A value of {@code 0} * will have no effect. * @return the new rank. */ public int addRank(E element, int gain) { Ranking ranking = this.map.get(element); if (ranking == null) { if (gain == 0) { return 0; } ranking = new Ranking(); this.map.put(element, ranking); } ranking.addRank(gain); return ranking.rank; } /** * This method gets the element that has currently the best (highest) positive {@link #getRank(Object) rank} . If * there are multiple best elements, it is unspecified which one is returned. * * @see #getBests() * * @return the element with the best positive rank or {@code null} if there is no element with a positive rank. */ public E getBest() { return getBest(1); } /** * This method gets the element that has currently the best (highest) {@link #getRank(Object) rank} greater or equal * to the given {@code threshold}. If there are multiple best elements, it is unspecified which one is returned. * * @see #getBests() * * @param threshold is the minimum rank accepted for the best element. * @return the element with the best rank greater or equal to the given {@code threshold} or {@code null} if there is * no such element. */ public E getBest(int threshold) { E best = null; int bestRank = threshold - 1; for (E element : this.map.keySet()) { Ranking ranking = this.map.get(element); if (ranking != null) { if (ranking.rank > bestRank) { best = element; bestRank = ranking.rank; } } } return best; } /** * This method gets a {@link List} containing the element(s) that has currently the best (highest) * {@link #getRank(Object) rank}. If there is no element with a positive rank, an empty {@link List} will be returned. * If there are multiple elements with the (same) {@link #getBest() best} rank, they will all be contained in the * returned {@link List}. * * @return the {@link List} with the best element(s). */ public List getBests() { List bests = new ArrayList<>(); int bestRank = RANK_UNACCEPTABLE + 1; for (E element : this.map.keySet()) { Ranking ranking = this.map.get(element); if (ranking != null) { if (ranking.rank > bestRank) { bests.clear(); bests.add(element); bestRank = ranking.rank; } else if (ranking.rank == bestRank) { bests.add(element); } } } return bests; } /** * This method gets all elements with a {@link #getRank(Object) rank} greater or equal to the given {@code threshold}. * * @param threshold is the minimum accepted {@link #getRank(Object) rank}. * @return the list with all elements better or equal to the given {@code threshold}. */ public List getBetterOrEqual(int threshold) { List bests = new ArrayList<>(); for (E element : this.map.keySet()) { Ranking ranking = this.map.get(element); if ((ranking != null) && (ranking.rank >= threshold)) { bests.add(element); } } return bests; } /** * This inner class represents the {@link #rank} of an element. */ private static class Ranking { /** The ranking value. */ private int rank; /** * @see RankMap#setUnacceptable(Object) */ public void setUnacceptable() { this.rank = RANK_UNACCEPTABLE; } /** * @return {@code true} if {@link RankMap#RANK_UNACCEPTABLE unacceptable}. */ public boolean isUnacceptable() { return (this.rank == RANK_UNACCEPTABLE); } /** * @see RankMap#addRank(Object, int) * * @param gain is the amount by which to increase the rank. */ public void addRank(int gain) { if ((this.rank != RANK_UNACCEPTABLE) && (gain != 0)) { int newRank = this.rank + gain; if (gain > 0) { if (newRank < this.rank) { // overflow newRank = Integer.MAX_VALUE; } } else { if ((newRank > this.rank) || (newRank == RANK_UNACCEPTABLE)) { newRank = RANK_UNACCEPTABLE + 1; } } this.rank = newRank; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy