Кратчайший способ получить Iterator по диапазону целых чисел в Java

Какой самый короткий способ получить итератор для диапазона целых чисел в Java? Другими словами, реализовать следующее:

/** 
* Returns an Iterator over the integers from first to first+count.
*/
Iterator<Integer> iterator(Integer first, Integer count);

Что-то типа

(first..first+count).iterator()

person cretzel    schedule 16.12.2008    source источник
comment
Есть ли причина, по которой вы не можете просто добавить 1 классическим способом? Просто из интереса   -  person chillysapien    schedule 16.12.2008
comment
Это фреймворк, который требует от меня написать итератор   -  person cretzel    schedule 16.12.2008
comment
Ясность превосходит краткость каждый раз, и существует множество доступных классов Range, которые включают итерацию.   -  person Martin of Hessle    schedule 15.03.2019


Ответы (7)


Непосредственное выполнение домашнего задания:

List<Integer> ints = new ArrayList<Integer>();
for (int i = 0; i < count; i++) {
    ints.add(first + i);
}
person Bombe    schedule 16.12.2008
comment
Это правильно, если вы серьезно относитесь к кратчайшему, за исключением того, что вам не хватает return ints.iterator(); ;-) - person Joachim Sauer; 16.12.2008
comment
Кроме того, это неэффективная память. - person Haroldo_OK; 20.10.2017
comment
Я думаю, что это немного снисходительно называть это домашним заданием. Это очень частый вариант использования, и, как показано ниже, есть более эффективные реализации, чем эта (хотя не все из них были возможны на момент написания). - person user23127; 31.05.2019

Эта реализация не требует памяти.

/**
 * @param begin inclusive
 * @param end exclusive
 * @return list of integers from begin to end
 */
public static List<Integer> range(final int begin, final int end) {
    return new AbstractList<Integer>() {
            @Override
            public Integer get(int index) {
                return begin + index;
            }

            @Override
            public int size() {
                return end - begin;
            }
        };
}

Edit:

В Java 8 и более поздних версиях вы можете просто сказать:

IntStream.range(begin, end).iterator()                // returns PrimitiveIterator.OfInt

или если вам нужна коробочная версия:

IntStream.range(begin, end).boxed().iterator()        // returns Iterator<Integer>
person Saintali    schedule 26.07.2011
comment
Хотя сам листинг довольно короткий, он возвращает список вместо Iterable/Iterator, который предлагает гораздо больше методов интерфейса. Здесь они не реализованы, и это, в свою очередь, может привести к неожиданным результатам, если экземпляр списка используется в непредвиденном контексте. Таким образом, можно сказать, что эта реализация нарушает контракт интерфейса List. Кроме того, реализация итератора в AbstractList не так проста, как могла бы быть, и использует больше переменных экземпляра, чем необходимо, поэтому требуется небольшой объем памяти. Поэтому я бы предпочел прямую реализацию Iterable/Iterator. - person Daniel Bimschas; 09.03.2015
comment
@DanielBimschas Думаю, это неправда. Этот класс придерживается контракта неизменяемого списка, как определено в JavaDoc для java.util.Collection. В частности, он выдает UnsupportedOperationException, если кто-то вызывает один из его деструктивных методов, например add() или remove(). Дополнительные сведения также см. в документе JavaDoc java.util.AbstractList. - person Saintali; 18.03.2015
comment
@Saintali Fair Point w.r.t. my нарушает комментарий контракта интерфейса списка. Пожалуйста, игнорируйте эту часть. ИМХО, другие мои комментарии остаются в силе. - person Daniel Bimschas; 20.03.2015
comment
Метод get() должен проверять, находится ли его параметр в допустимом диапазоне, и если нет (например, отрицательный или больше размера), выдать IndexOutofBoundsException. - person Kevin; 27.01.2016
comment
Становиться придирчивым. Список действительно имеет объем памяти (begin и end), но он постоянен. - person cambunctious; 03.01.2019

Непроверенный. Отображение этого на «min, count» оставлено читателю в качестве упражнения.

public class IntRangeIterator implements Iterator<Integer> {
  private int nextValue;
  private final int max;
  public IntRangeIterator(int min, int max) {
    if (min > max) {
      throw new IllegalArgumentException("min must be <= max");
    }
    this.nextValue = min;
    this.max = max;
  }

  public boolean hasNext() {
    return nextValue <= max;
  }

  public Integer next() {
    if (!hasNext()) {
      throw new NoSuchElementException();
    }
    return Integer.valueOf(nextValue++);
  }

  public void remove() {
    throw new UnsupportedOperationException();
  }
}
person Joachim Sauer    schedule 16.12.2008
comment
это выглядит почти идентично тому, что я только что написал для себя :) - person Alnitak; 13.02.2015

Если вам действительно нужен кратчайший объем кода, то ответ Бомбы подойдет. Тем не менее, он сосет память без уважительной причины. Если вы хотите реализовать это самостоятельно, это будет что-то вроде:

import java.util.*;

public class IntegerRange implements Iterator<Integer>
{
    private final int start;
    private final int count;

    private int position = -1;

    public IntegerRange(int start, int count)
    {
        this.start = start;
        this.count = count;
    }

    public boolean hasNext()
    {
        return position+1 < count;
    }

    public Integer next()
    {
        if (position+1 >= count)
        {
            throw new NoSuchElementException();
        }
        position++;
        return start + position;
    }

    public void remove()
    {
        throw new UnsupportedOperationException();
    }
}
person Jon Skeet    schedule 16.12.2008

Пример использования фреймворка guava. Обратите внимание, что это не материализует набор (хотя вам нужно прочитать реализацию ContiguousSet, чтобы убедиться в этом).

import com.google.common.collect.ContiguousSet;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.DiscreteDomains;

class RangeIterator { 

    public Iterator<Integer> range(int start, int length) {
        assert length > 0;
        Range<Integer> dim_range = Ranges.closedOpen(start, start + length);
        DiscreteDomain<Integer> ints = DiscreteDomains.integers();
        ContiguousSet<Integer> dim = dim_range.asSet(ints);
        return dim.iterator();
    }
}
person Lee    schedule 31.10.2011
comment
самая короткая версия этого: Iterator<Integer> range = Ranges.closedOpen(start, end).asSet(DiscreteDomains.integers()).iterator(); - person Miguel; 18.10.2012
comment
@Miguel Метод Range#asSet(DiscreteDomain) устарело и было удалено в Guava 16. - person Synoli; 14.03.2017
comment
@Synoli Iterator<Integer> range = ContiguousSet.create(Ranges.closedOpen(start, end), DiscreteDomains.integers()).iterator(); - person Miguel; 29.03.2017

Пример использования потокового API в java 8:

int first = 0;
int count = 10;
Iterator<Integer> it = IntStream.range(first, first + count).iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

Без итератора это может быть:

int first = 0;
int count = 10;
IntStream.range(first, first + count).forEach(i -> System.out.println(i));
person btpka3    schedule 20.04.2016

Обычно считается хорошим стилем передавать Collection и друзьям вместо Iterator (см. эту запись часто задаваемых вопросов), поэтому я бы рекомендовал что-то вроде

public final class IntegerRange implements Set<Integer> {
        final LinkedHashSet<Integer> backingList;
        public IntegerRange(final int start, final int count) {
                backingList = new LinkedHashSet(count, 1.0f);
                for (int i=0; i < count; i++) {
                        backingList.set(i, start + i);
                }       
        }       
        /** Insert a bunch of delegation methods here */
}

а затем просто используйте .iterator(), когда вам нужно передать Iterator в любую используемую вами структуру.

ОБНОВЛЕНИЕ: очевидно, что этот код не ленив. Если вы не можете позволить себе дополнительные накладные расходы памяти для хранения (потенциально) 2 ^ 32-1 Integers, вам следует использовать другое решение. Кроме того, ничего о типе не гарантирует, что диапазон будет отсортирован (даже если это зависит от реализации). Если вам нужно гарантировать сортировку, вы можете изучить реализацию SortedSet и поддерживает его TreeSet, но построение диапазона займет больше времени. Честно говоря, если вы так заинтересованы в правильности деталей, возможно, вам стоит поискать библиотеку. Tapestry имеет внутреннюю версию, например.

person Hank Gay    schedule 16.12.2008
comment
Зачем реализовывать это в наборе поддержки? Это довольно легко реализовать, не сохраняя все целые числа (что может быть довольно интенсивным в памяти), если вам не нужно поддерживать add() и remove(). Я бы также добавил, что список был бы более подходящим (поскольку определенно есть какой-то порядок) - person Joachim Sauer; 16.12.2008
comment
RE: List -> cretzel не указал ListIterator, а Iterator не гарантирует порядок, поэтому я выбрал ближайшее сопоставление (Collection), хотя, оглядываясь назад, я бы использовал Set. RE: ленивый -› это казалось гораздо более трудоемким, плюс cretzel не упомянул, что не нуждается в поддержке remove(). - person Hank Gay; 16.12.2008
comment
Запись часто задаваемых вопросов, на которую вы ссылаетесь, говорит нам, что мы не должны передавать итераторы вокруг набора объектов. Но если вас интересует не коллекция, а процесс итерации, передача Iterator вполне допустима. - person Guillaume; 16.12.2008
comment
Я думаю, что Collection vs. Iterator действительно зависит. Ребята из Google, кажется, предпочитают Iterator для своих приложений: см. Почему так много внимания уделяется Iterators и Iterables? здесь: code.google.com/p/google-collections/wiki/Faq< /а> - person Dave Ray; 17.12.2008