Java PriorityQueue с фиксированным размером
Я вычисляю большое количество возможных результирующих комбинаций альгортимов. Чтобы сортировать эти комбинации, я оцениваю их с двойным значением и сохраняю их в PriorityQueue. В настоящее время в этой очереди находится около 200 тыс. Элементов, что в значительной степени интеллигентно. По сути, мне нужно только сказать лучшие 1000 или 100 из всех элементов в списке.
Поэтому я только начал спрашивать себя, есть ли способ иметь очередь приоритетов с фиксированным размером в Java. Я должен вести себя так:
Является ли предмет лучше, чем один из уже сохраненных? Если да, вставьте его в соответствующее положение и выбросьте элемент с наименьшим рейтингом.
Есть ли у кого-нибудь идеи? Еще раз спасибо!
Marco
Ответы
Ответ 1
que.add(d);
if (que.size() > YOUR_LIMIT)
que.poll();
или я пропустил ваш вопрос?
edit: забыл упомянуть, что для этого вам, вероятно, придется инвертировать функцию compareTo, поскольку она будет выбрасывать ту, которая имеет самый высокий приоритет для каждого цикла. (если a "лучше" b compare (a, b) должно возвращать положительное число.
Например, чтобы сохранить самые большие числа, используйте что-то вроде этого:
public int compare(Double first, Double second) {
// keep the biggest values
return first > second ? 1 : -1;
}
Ответ 2
MinMaxPriorityQueue
, Google Guava
Существует действительно класс для поддержания очереди, который при добавлении элемента, который будет превышать максимальный размер коллекции, сравнивает элементы, чтобы найти элемент для удаления и тем самым создать комнату: MinMaxPriorityQueue
находится в Google Гуава с версии 8.
EvictingQueue
Кстати, если вы просто хотите удалить самый старый элемент без какого-либо сравнения значений объектов, Google Guava 15 получил класс EvictingQueue
.
Ответ 3
В Apache Lucene есть очередь приоритетов с фиксированным размером: http://lucene.apache.org/java/2_4_1/api/org/apache/lucene/util/PriorityQueue.html
Он имеет отличную производительность, основанную на моих тестах.
Ответ 4
Кажется естественным просто держать верхнюю 1000 каждый раз, когда вы добавляете элемент, но PriorityQueue
не предлагает ничего для достижения этого изящно. Возможно, вы можете вместо PriorityQueue
сделать что-то вроде этого в методе:
List<Double> list = new ArrayList<Double>();
...
list.add(newOutput);
Collections.sort(list);
list = list.subList(0, 1000);
Ответ 5
Использовать 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);
}
Ответ 6
Просто poll()
очередь, если ее наименьший элемент меньше (в вашем случае имеет худший рейтинг, чем) текущий элемент.
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;
}
Это предполагает, что у вас есть какой-то класс комбинации, который реализует Comparable
, который сравнивает комбинации по их рейтингу.
Изменить: Чтобы уточнить, Iterable
в моем примере не нужно заполнять заранее. Например, здесь Iterable<Integer>
, который даст вам все натуральные числа, а int
может представлять:
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();
}
};
}
};
Потребление памяти очень скромно, как вы можете видеть - для более чем двух миллиардов значений вам нужны два объекта (Iterable
и Iterator
) плюс один int
.
Конечно, вы можете легко адаптировать мой код, чтобы он не использовал Iterable
- я просто использовал его, потому что это элегантный способ представления последовательности (также, я делал слишком много Python и С# ☺).
Ответ 7
Лучшим подходом было бы более жесткое смягчение того, что происходит в очереди, удаление и добавление к нему по мере запуска программы. Похоже, будет некоторая комната, чтобы исключить некоторые элементы, прежде чем добавлять их в очередь. Это было бы проще, чем изобретать колесо так, чтобы он говорил.