Получить размер итерации в Java
мне нужно выяснить количество элементов в Iterable
в Java.
Я знаю, что могу это сделать:
Iterable values = ...
it = values.iterator();
while (it.hasNext()) {
it.next();
sum++;
}
Я также мог бы сделать что-то подобное, потому что мне больше не нужны объекты в Iterable:
it = values.iterator();
while (it.hasNext()) {
it.remove();
sum++;
}
небольшой масштабный тест не показал большой разницы в производительности, каких-либо комментариев или других идей для этой проблемы?
9 ответов
TL; DR: используйте метод утилиты Iterables.size(Iterable)
великого гуавы библиотека.
из двух фрагментов кода, Вы должны использовать первый, потому что второй будет удалить все элементы из values
, поэтому после этого он пуст. Изменение структуры данных для простого запроса, такого как его размер, очень неожиданно.
для производительности это зависит от структуры данных. Если это, например, на самом деле ArrayList
, удаление элементов с самого начала (что делает ваш второй метод) очень медленно (вычисление размера становится O(n*n) вместо O(n), как должно быть).
в общем, если есть шанс, что values
на самом деле Collection
и не только Iterable
, проверьте это и называют size()
в случае:
if (values instanceof Collection<?>) {
return ((Collection<?>)values).size();
}
// use Iterator here...
вызов size()
обычно будет намного быстрее, чем подсчет количества элементов, и этот трюк именно то, что Iterables.size(Iterable)
of гуавы делает для вас.
если вы работаете с Java 8 вы можете использовать:
Iterable values = ...
long size = values.spliterator().getExactSizeIfKnown();
он будет работать только в том случае, если итерационный источник имеет определенный размер. Большинство Spliterators для коллекции, но вы можете иметь проблемы, если это HashSet
или ResultSet
например.
вы можете проверить javadoc здесь.
если Java 8 не является опцией, или если вы не знаете, откуда исходит iterable, вы можете использовать тот же подход, что и гуава:
if (iterable instanceof Collection) {
return ((Collection<?>) iterable).size();
} else {
int count = 0;
Iterator iterator = iterable.iterator();
while(iterator.hasNext()) {
iterator.next();
count++;
}
return count;
}
это, возможно, немного поздно, но может помочь кому-то. Я сталкиваюсь с подобной проблемой с Iterable
в моей кодовой базе и решении было использовать for each
без явного вызова values.iterator();
.
int size = 0;
for(T value : values) {
size++;
}
строго говоря, Iterable не имеет размера. Подумайте о структуре данных как о цикле.
и думать о следующем экземпляре повторяемое, размер:
new Iterable(){
@Override public Iterator iterator() {
return new Iterator(){
@Override
public boolean hasNext() {
return isExternalSystemAvailble();
}
@Override
public Object next() {
return fetchDataFromExternalSystem();
}};
}};
вы можете привести свой iterable в список, а затем использовать .размер() на нем.
Lists.newArrayList(iterable).size();
для ясности вышеуказанный метод потребует следующего импорта:
import com.google.common.collect.Lists;
Я бы пошел на it.next()
по той простой причине, что next()
гарантированно будет реализован, в то время как remove()
- необязательная операция.
E next()
возвращает следующий элемент в итерации.
void remove()
удаляет из базовой коллекции последний элемент, возвращенный iterator (дополнительная работа).
Что касается меня, это просто разные методы. Первый оставляет объект, на котором вы выполняете итерацию, неизменным, в то время как секунды оставляют его пустым. Вопрос в том, что вы хотите сделать. Сложность удаления основана на реализации вашего iterable объекта. Если вы используете коллекции-просто получите размер, как было предложено Kazekage Gaara-его обычно лучшая производительность подхода.
вместо использования циклов и подсчета каждого элемента или использования и сторонней библиотеки мы можем просто напечатать iterable в ArrayList и получить его размер.
((ArrayList) iterable).size();
почему бы вам просто не использовать size()
метод Collection
получить количество элементов?
Iterator
просто предназначен для итерации, ничего больше.