Ограничить ListIterator на первые N элементов (оптимизировано)

110
13

Что такое простой и быстрый способ получить итератор, который возвращает не более N элементов с начала List?


Простейшими версиями, которые я мог бы придумать, являются:


# 1:


import com.google.common.collect.Iterators;

// ...

public static <E> Iterator<E> lengthLimitedIterator(Iterable<E> source, int maxLen) {
return Iterators.partition(source.iterator(), maxLen).next().iterator();
}


# 2:


public static <E> Iterator<E> lengthLimitedIterator(List<E> source, int maxLen) {
return source.subList(0, Math.min(source.size(), maxLen)).iterator();
}

К сожалению, обе версии создают временный List, который существенно влияет на производительность, поскольку я вызываю этот метод миллионы раз в сжатом цикле.


Существуют ли какие-либо другие библиотечные функции, которые я мог бы использовать для этого?


Примечание. Я не могу избежать итерации по списку, поскольку передаю его методу, который принимает итератор в качестве аргумента, и я не могу изменить этот класс.

спросил(а) 2009-10-24T21:56:00+04:00 10 лет, 8 месяцев назад
1
Решение
115

Похоже, что функция

ответил(а) 2010-08-05T04:29:00+04:00 9 лет, 11 месяцев назад
148

Вы уже знаете список, поэтому вы можете просто вызвать метод List.subList(int fromIndex, int toIndex). В соответствии со спецификацией, subList поддерживается исходным списком, поэтому он не создает полномасштабный List, просто какой-то прокси-объект.

ответил(а) 2009-10-24T22:16:00+04:00 10 лет, 8 месяцев назад
95

Почему бы просто не


list.subList(0, 42).iterator();

Я не уверен, почему вы думаете о создании этого временного списка. Это не делает ничего, что я считаю дорогим. Фактически, создание этого списка намного дешевле, чем повторение его, что я предполагаю, что вы это делаете.

ответил(а) 2009-10-24T22:14:00+04:00 10 лет, 8 месяцев назад
94

Это место, где Decorator работает очень хорошо: ваш декоратор хранит счет, который увеличивается на next(), и используется контролем hasNext().


Пример (намеренно неполный):


public class LengthLimitedIterator<T>
implements Iterator<T>
{
private Iterator<T> _wrapped;
private int _length;
private int _count;

public LengthLimitedIterator(Iterator<T> wrapped, int length)
{
_wrapped = wrapped;
_length = length;
}

public boolean hasNext()
{
if (_count < _length)
return _wrapped.hasNext();
return false;
}

public T next()
{
// FIXME - add exception if count >= length
_count++;
return _wrapped.next();
}

ответил(а) 2009-10-24T21:59:00+04:00 10 лет, 8 месяцев назад
67

Метод ArrayList.sublist(int,int) не создает копию исходного списка. Вместо этого он возвращает экземпляр SubList, который обертывает исходный ArrayList. Итератор, возвращенный сублистом, полученным из массива, также не делает копию.


Поэтому я советую использовать ArrayList в качестве типа вашего базового списка и метода sublist. Если это не так быстро, реализуйте свой собственный вариант ArrayList, который реализует метод limitedLengthIterator. Например, вы должны быть в состоянии избавиться от кода, который проверяет наличие параллельных изменений.

ответил(а) 2009-10-25T17:27:00+03:00 10 лет, 8 месяцев назад
56

Если вас беспокоит производительность, не используйте Iterator, используйте индекс в массиве. Это даст гораздо лучшую производительность. Получение первых N элементов массива тривиально.

ответил(а) 2009-10-25T17:01:00+03:00 10 лет, 8 месяцев назад
39

Эта версия оказывается быстрее, чем любой другой пример:


public static <E> Iterator<E> lengthLimitedIterator(List<E> source, int maxLen) {
maxLen = Math.min(maxLen, source.size());
ArrayList<E> tempList = new ArrayList<E>(maxLen);
for (int i = 0; i < maxLen; ++ i) {
tempList.add(source.get(i));
}
return tempList.iterator();
}

Если временный список должен быть создан в любом случае, ArrayList выполняется быстрее, чем декорированные списки, возвращаемые другими библиотечными методами.


Я предполагаю, что ArrayList получает специальное лечение внутри виртуальной машины.


Возможно, это было бы неэффективно для очень длинных списков, но мои списки короткие (почти всегда меньше 50 элементов).

ответил(а) 2009-10-25T15:17:00+03:00 10 лет, 8 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема