Статьи

Очень жаль, что в Java 8 нет Iterable.stream ()

Это один из наиболее интересных недавних вопросов переполнения стека:

Почему Iterable не предоставляет методы stream () и parallelStream ()?

Поначалу может показаться интуитивно понятным сделать преобразование Iterable в Stream простым, поскольку эти два варианта более или менее одинаковы для 90% всех вариантов использования.

Конечно, группа экспертов уделяла большое внимание созданию параллельного интерфейса API Stream , но любой, кто работает с Java каждый день, сразу же заметит, что Stream наиболее полезен в последовательной форме . И Iterable — это просто так. Последовательный поток без каких-либо гарантий в отношении распараллеливания. Таким образом, это было бы интуитивно понятно, если бы мы могли просто написать:

1
iterable.stream();

На самом деле, подтипы Iterable имеют такие методы, например:

1
collection.stream();

Брайан Гетц сам дал ответ на вопрос о переполнении стека . Причины этого упущения коренятся в том факте, что некоторые Iterables могут предпочесть вернуть IntStream вместо Stream . Это действительно кажется очень отдаленной причиной для принятия дизайнерского решения, но, как всегда, сегодняшнее пропущение не означает упущение навсегда. С другой стороны, если бы они сегодня внедрили Iterable.stream() , и это оказалось ошибкой, они не могли бы удалить его снова.

Что ж, примитивные типы в Java — это боль, и в первую очередь они делали всякие плохие вещи с дженериками, а теперь и с Stream , а мы должны написать следующее, чтобы превратить Iterable в Stream :

1
Stream s = StreamSupport.stream(iterable.spliterator(), false);

Брайан Гетц утверждает, что это «легко», но я бы не согласился. Как потребитель API, я испытываю большие трения в производительности из-за:

  • StreamSupport этого бесполезного в противном случае типа StreamSupport . Этот метод вполне можно было бы поместить в интерфейс Stream , потому что у нас уже есть методы построения Stream , такие как Stream.of() .
  • Необходимость помнить тонкую разницу между Iterator и Spliterator в контексте того, что я считаю, не имеет ничего общего с распараллеливанием. Вполне возможно, что в конечном итоге Spliterators станут популярными, так что это сомнение для магического шара.
  • На самом деле, я должен повторить информацию, что нечего распараллеливать через логический аргумент false

Параллелизация действительно имеет такой большой вес в этом новом API, даже если она будет охватывать только около 5% -10% всех операций манипулирования функциональной коллекцией. Хотя последовательная обработка не была главной целью разработки API JDK 8, она действительно является основным преимуществом для всех нас, и трения вокруг API, связанные с последовательной обработкой, должны быть как можно ниже.

Вышеуказанный метод должен был быть только что вызван:

1
Stream s = Stream.stream(iterable);

Это может быть реализовано так:

1
2
3
public static<T> Stream<T> stream(Iterable<T> i) {
    return StreamSupport.stream(i.spliterator(), false);
}

Очевидно с удобными перегрузками, которые допускают дополнительные специализации, такие как распараллеливание или передача Spliterator

Но опять же, если бы Iterable имел собственный метод по умолчанию stream() , невероятное количество API-интерфейсов было бы намного лучше интегрировано с Java 8 из коробки, даже без явной поддержки Java 8!

Взять, к примеру, JOOQ . jOOQ по-прежнему поддерживает Java 6, поэтому прямая зависимость невозможна. Однако тип ResultQuery в ResultQuery является Iterable . Это позволяет вам использовать такие запросы непосредственно в циклах foreach, как если бы вы писали PL / SQL:

PL / SQL

1
2
3
4
5
6
FOR book IN (
  SELECT * FROM books ORDER BY books.title
)
LOOP
  -- Do things with book
END LOOP;

Ява

1
2
3
4
5
for (BookRecord book :
  ctx.selectFrom(BOOKS).orderBy(BOOKS.TITLE)
) {
  // Do things with book
}

Теперь представьте то же самое в Java 8:

1
2
3
ctx.selectFrom(BOOKS).orderBy(BOOKS.TITLE)
   .stream()
   .map / reduce / findAny, etc...

К сожалению, вышесказанное в настоящее время невозможно. Конечно, вы можете с нетерпением извлечь все результаты в jOOQ Result , который расширяет List :

1
2
3
4
ctx.selectFrom(BOOKS).orderBy(BOOKS.TITLE)
   .fetch()
   .stream()
   .map / reduce / findAny, etc...

Но это еще один метод для вызова (каждый раз), и фактическая семантика потока нарушена, потому что выборка выполняется с нетерпением.

Жаловаться на высоком уровне

Это, конечно, жалоба на высоком уровне, но было бы действительно здорово, если бы будущая версия Java, например Java 9, добавила этот недостающий метод в Iterable API. Опять же, 99% всех вариантов использования будут хотеть возвращать тип Stream , а не тип IntStream . И если они хотят этого по какой-то непонятной причине (гораздо более непонятной, чем многие злые вещи из старых унаследованных API-интерфейсов Java, глядя на ваш Calendar ), то почему бы им просто не объявить метод intStream() . В конце концов, если кто-то достаточно сумасшедший, чтобы написать Iterable<Integer> когда он действительно работает с примитивными типами int , он, вероятно, примет небольшой обходной путь.

Ссылка: Очень жаль, что Java 8 не имеет Iterable.stream () от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и AND JOOQ .