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.

Ответ 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

Лучшим подходом было бы более жесткое смягчение того, что происходит в очереди, удаление и добавление к нему по мере запуска программы. Похоже, будет некоторая комната, чтобы исключить некоторые элементы, прежде чем добавлять их в очередь. Это было бы проще, чем изобретать колесо так, чтобы он говорил.