Je calcule un grand nombre de combinaisons résultantes possibles d'un algortihm. Pour trier ces combinaisons, je les note avec une valeur double et les stocke dans PriorityQueue. Actuellement, il y a environ 200 000 éléments dans cette file d'attente, ce qui est à peu près intensif en mémoire. En fait, je n'ai besoin que de dire les 1000 ou 100 meilleurs éléments de la liste. J'ai donc juste commencé à me demander s'il y avait un moyen d'avoir une file d'attente prioritaire avec une taille fixe en Java. Je dois me comporter comme ceci: l'article est-il meilleur que l'un des déjà stockés? Si oui, insérez-le à la position correspondante et jetez l'élément le moins bien noté.
Est-ce que quelqu'un a une idée? Merci encore beaucoup!
Marco
que.add(d);
if (que.size() > YOUR_LIMIT)
que.poll();
ou ai-je mal compris votre question?
edit: oublié de mentionner que pour que cela fonctionne, vous devez probablement inverser votre fonction comparTo car elle jettera celle qui a la priorité la plus élevée à chaque cycle. (si a est "meilleur" b, comparer (a, b) devrait renvoyer un nombre positif.
exemple pour garder les plus grands nombres utiliser quelque chose comme ceci:
public int compare(Double first, Double second) {
// keep the biggest values
return first > second ? 1 : -1;
}
MinMaxPriorityQueue
, Google GuavaIl existe en effet une classe pour maintenir une file d'attente qui, lors de l'ajout d'un élément qui dépasserait la taille maximale de la collection, compare les éléments pour trouver un élément à supprimer et ainsi créer de l'espace: MinMaxPriorityQueue
trouvé dans Google Guava à partir de la version 8.
Soit dit en passant, si vous souhaitez simplement supprimer l'élément le plus ancien sans comparer les valeurs des objets, Google Guava 15 a obtenu la classe EvictingQueue
.
Il y a une file d'attente prioritaire de taille fixe dans Apache Lucene: http://lucene.Apache.org/Java/2_4_1/api/org/Apache/lucene/util/PriorityQueue.html
Il a d'excellentes performances sur la base de mes tests.
Utilisez SortedSet:
SortedSet<Item> items = new TreeSet<Item>(new Comparator<Item>(...));
...
void addItem(Item newItem) {
if (items.size() > 100) {
Item lowest = items.first();
if (newItem.greaterThan(lowest)) {
items.remove(lowest);
}
}
items.add(newItem);
}
Juste poll()
la file d'attente si son moindre élément est inférieur (dans votre cas, a une note inférieure à) l'élément actuel.
static <V extends Comparable<? super V>>
PriorityQueue<V> nbest(int n, Iterable<V> valueGenerator) {
PriorityQueue<V> values = new PriorityQueue<V>();
for (V value : valueGenerator) {
if (values.size() == n && value.compareTo(values.peek()) > 0)
values.poll(); // remove least element, current is better
if (values.size() < n) // we removed one or haven't filled up, so add
values.add(value);
}
return values;
}
Cela suppose que vous disposez d'une sorte de classe de combinaison qui implémente Comparable
qui compare les combinaisons sur leur note.
Edit: Juste pour clarifier, le Iterable
dans mon exemple n'a pas besoin d'être prérempli. Par exemple, voici un Iterable<Integer>
qui vous donnera tous les nombres naturels qu'un int
peut représenter:
Iterable<Integer> naturals = new Iterable<Integer>() {
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
int current = 0;
@Override
public boolean hasNext() {
return current >= 0;
}
@Override
public Integer next() {
return current++;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
La consommation de mémoire est très modeste, comme vous pouvez le voir - pour plus de 2 milliards de valeurs, vous avez besoin de deux objets (le Iterable
et le Iterator
) plus un int
.
Vous pouvez bien sûr adapter assez facilement mon code pour qu'il n'utilise pas de Iterable
- Je l'ai utilisé parce que c'est une manière élégante de représenter une séquence (aussi, j'en fais trop Python et C # ☺).
Il semble naturel de simplement conserver les 1000 premiers à chaque fois que vous ajoutez un élément, mais le PriorityQueue
n'offre rien pour y parvenir gracieusement. Peut-être que vous pouvez, au lieu d'utiliser un PriorityQueue
, faire quelque chose comme ça dans une méthode:
List<Double> list = new ArrayList<Double>();
...
list.add(newOutput);
Collections.sort(list);
list = list.subList(0, 1000);
Une meilleure approche serait de modérer plus étroitement ce qui se passe dans la file d'attente, en le supprimant et en y ajoutant pendant l'exécution du programme. Il semble qu'il y aurait de la place pour exclure certains éléments avant de les ajouter dans la file d'attente. Ce serait plus simple que de réinventer la roue pour ainsi dire.