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

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

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

Что-то вроде

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

Есть ли причина, по которой вы не можете просто «добавить 1» в классическом стиле? Просто интересно

chillysapien 16.12.2008 14:19

Это фреймворк, который требует, чтобы я написал итератор.

cretzel 16.12.2008 14:41

Ясность всегда превосходит краткость, и существует множество доступных классов Range, которые включают итерацию.

Martin of Hessle 15.03.2019 14:50
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
25
3
37 517
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Ответ принят как подходящий

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

List<Integer> ints = new ArrayList<Integer>();
for (int i = 0; i < count; i++) {
    ints.add(first + i);
}

Это правильный вариант, если вы серьезно относитесь к «кратчайшему», за исключением того, что вам не хватает «return ints.iterator ();» ;-)

Joachim Sauer 16.12.2008 14:59

Кроме того, это нехватка памяти.

Haroldo_OK 20.10.2017 13:26

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

user23127 31.05.2019 14:48

Не проверено. Сопоставление этого с «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();
  }
}

это выглядит почти идентично тому, что я только что написал для себя :)

Alnitak 13.02.2015 17:25

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

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();
    }
}

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

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 Integer, вам следует использовать другое решение. Кроме того, ничто в отношении типа не гарантирует, что диапазон будет отсортирован (даже если это так, в зависимости от реализации). Если вам нужно гарантировать сортировку, вы можете рассмотреть возможность реализации SortedSet и поддержки его с помощью TreeSet, но для построения диапазона потребуется больше времени. Честно говоря, если вы так озабочены тем, чтобы детали были верны, возможно, стоит поискать библиотеку. Например, у гобелена есть внутренняя версия.

Зачем реализовывать это в наборе подложек? Это довольно легко реализовать, не сохраняя все целые числа (что может быть довольно интенсивным для памяти), если вам не нужно поддерживать add () и remove (). Я бы также добавил, что список был бы более подходящим (поскольку определенно есть некоторый порядок)

Joachim Sauer 16.12.2008 15:13

RE: List -> cretzel не указал ListIterator, а Iterator не дает никаких гарантий упорядочивания, поэтому я выбрал ближайшее сопоставление (Collection), хотя, оглядываясь назад, я бы использовал Set. RE: lazy -> казалось, что это намного больше работы, плюс Кретцель не упомянул о том, что не нуждается в поддержке remove ().

Hank Gay 16.12.2008 15:23

Запись в FAQ, на которую вы ссылаетесь, говорит нам, что мы не должны передавать итераторы вокруг коллекции объектов. Но если вас интересует не коллекция, а процесс итерации, передача Iterator вполне нормальна.

Guillaume 16.12.2008 15:53

Я думаю, что Collection vs. Iterator действительно зависит. Ребята из Google, кажется, предпочитают Iterator для своих приложений: см. «Почему так много внимания уделяется итераторам и Iterables?» здесь: code.google.com/p/google-collections/wiki/Faq

Dave Ray 17.12.2008 02:54

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

/**
 * @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;
            }
        };
}

Редактировать:

В Java 8 вы можете просто сказать:

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

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

IntStream.range(begin, end).boxed().iterator()        // returns Iterator<Integer>

Хотя сам список довольно короткий, он возвращает List вместо Iterable / Iterator, который предлагает гораздо больше методов интерфейса. Они здесь не реализованы, и это, в свою очередь, может привести к неожиданным результатам, если экземпляр списка используется в «непредвиденном» контексте. Таким образом, можно сказать, что эта реализация нарушает контракт интерфейса List. Кроме того, реализация итератора в AbstractList не так проста, как могла бы быть, и использует больше переменных экземпляра, чем необходимо, поэтому требуется небольшой объем памяти. Поэтому я бы предпочел прямую реализацию Iterable / Iterator.

Daniel Bimschas 09.03.2015 14:13

@DanielBimschas Я думаю, это неправда. Этот класс действительно придерживается контракта списка неизменяемый, как определено в JavaDoc для java.util.Collection. В частности, он выбрасывает UnsupportedOperationException, если кто-то называет один из его «деструктивных» методов, например add() или remove(). Для получения дополнительных сведений см. Также JavaDoc java.util.AbstractList.

Saintali 18.03.2015 04:44

@Saintali fair point w.r.t. мой комментарий "нарушает контракт интерфейса списка". Пожалуйста, не обращайте внимания на эту часть. ИМХО мои другие комментарии остаются в силе.

Daniel Bimschas 20.03.2015 12:00

Метод get() должен проверить, находится ли его параметр в допустимом диапазоне, а если нет (например, отрицательное значение или больше размера), бросить IndexOutofBoundsException.

Kevin 27.01.2016 22:14

Придираться. Список делает имеет след памяти (begin и end), но он постоянный.

cambunctious 03.01.2019 21:31

Пример использования фреймворка 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();
    }
}

самая короткая версия этого: Iterator<Integer> range = Ranges.closedOpen(start, end).asSet(DiscreteDomains.integers()).iterator();

Miguel 18.10.2012 01:39

@Miguel Метод Range#asSet(DiscreteDomain)устарела и была удалена в Guava 16.

Synoli 14.03.2017 17:32

@Synoli Iterator<Integer> range = ContiguousSet.create(Ranges.closedOpen(start, end), DiscreteDomains.integers()).iterator();

Miguel 29.03.2017 20:39

Пример использования потокового 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));

Другие вопросы по теме