Arhn - архитектура программирования

В чем разница между Stream.of и IntStream.range?

Пожалуйста, рассмотрите этот код:

System.out.println("#1");
Stream.of(0, 1, 2, 3)
        .peek(e -> System.out.println(e))
        .sorted()
        .findFirst();

System.out.println("\n#2");
IntStream.range(0, 4)
        .peek(e -> System.out.println(e))
        .sorted()
        .findFirst();

Вывод будет:

#1
0
1
2
3

#2
0

Может ли кто-нибудь объяснить, почему вывод двух потоков отличается?

24.05.2021

  • Интересная оптимизация! Я полагаю, что .sorted() на этом IntStream просто нет, потому что он уже каким-то образом знает, что он уже отсортирован. 24.05.2021
  • Вы также можете проверить docs.oracle.com/javase/8/docs/api/java/util/stream/ о том, как потоки лениво оцениваются, а не просто выполняются сразу же как можно скорее. 24.05.2021
  • Как правило, то, будет ли peek фактически наблюдать данный элемент, непредсказуемо и зависит от реализации. Он предназначен только для целей отладки. 24.05.2021
  • Кроме того, разница, о которой вы спрашиваете и на которую дан ответ, еще одна разница заключается в том, что Stream.of(0, 1, 2, 3) дает вам Stream<Integer>, тогда как IntStream.range(0, 4) дает вам IntStream, поток примитивов int. 24.05.2021
  • @ kaya3 Разве проблема не в том, что поток ленив и оценивается только первый элемент, как упоминал Прогман? Было бы странно, если бы peek случайно решил не работать или чтобы он работал только иногда. 25.05.2021
  • @FilipeRodrigues Вы и Кайя говорите одно и то же, только разными словами. 25.05.2021
  • Я говорю это, но я также говорю, что это деталь реализации, независимо от того, потребляется ли элемент, за исключением нескольких методов потока, которые это определяют. peek было бы в пределах спецификации, чтобы делать много разных вещей во многих распространенных сценариях. 25.05.2021
  • @ sp00m, и все же он не может выполнить max или min в O(1) ... IntStream.range(0, 4).peek(System.out::println).max(); // produces 0,1,2,3. Почему бы не использовать ту же оптимизацию здесь? 26.05.2021
  • @Naman Несмотря на то, что IntStream создает поток в порядке возрастания, обычно отсортированный поток может быть в порядке возрастания или убывания. В таком случае мы все еще можем получить findFirst за O(1), но для max/min ответом может быть либо самый левый, либо самый правый ответ, верно? 26.05.2021
  • @GauthamM После публикации комментария я как бы понял, что я растянул часть, включив сложность в обсуждение. Чтобы конкретно ответить на ваш вопрос, для отсортированного потока, даже не зная порядка, min/max будет сравнением первого и последнего значения, так что все равно O(1). Но опять же, я думаю, что мне не следовало растягивать часть сложности в моем предыдущем комментарии. Так что мы можем избежать обсуждения этого в этом контексте (что в основном не имеет значения). 26.05.2021
  • @Naman Да, min/max также требует одного шага. Но возможно ли, чтобы поток напрямую получал n-й/последний элемент? 26.05.2021

Ответы:


1

Итак, IntStream.range() возвращает a sequential ordered IntStream from startInclusive(inclusive) to endExclusive (exclusive) by an incremental step of 1, что означает, что он уже отсортирован. Поскольку он уже отсортирован, логично, что следующая .sorted() промежуточная операция ничего не делает. В результате peek() выполняется только для первого элемента (поскольку для терминальной операции требуется только первый элемент).

С другой стороны, элементы, переданные в Stream.of(), не обязательно сортируются (и метод of() не проверяет, отсортированы ли они). Следовательно, .sorted() должен пройти через все элементы, чтобы создать отсортированный поток, что позволяет терминальной операции findFirst() вернуть первый элемент отсортированного потока. В результате peek выполняется для всех элементов, хотя терминальной операции нужен только первый элемент.

24.05.2021
  • 2 вещи: 1. это оптимизация компилятора? похоже, вы это подразумеваете. 2. Было бы полезно, если бы вы подробнее рассказали о том, как sorted() изменить ситуацию (для просмотра), потребовав все элементы... Хороший ответ, кстати. 24.05.2021
  • @ernest_k 1. Если подумать, оптимизация компилятора кажется менее вероятной. Я думаю, что часть or at least does nothing более вероятна. 2. какой проработки, по вашему мнению, не хватает? Я написал во втором абзаце, почему sorted обычно нужно пройти по всем элементам. 24.05.2021
  • @ernest_k это только оптимизация во время выполнения, и к тому же она немного хрупкая. 24.05.2021

  • 2

    IntStream.range уже отсортировано:

    // reports true
    System.out.println(
           IntStream.range(0, 4)
                    .spliterator()
                    .hasCharacteristics(Spliterator.SORTED)
    );
    

    Поэтому, когда метод sorted() в потоке срабатывает, внутри он стать NO-OP.

    В противном случае, как вы уже видите в вашем первом примере, все элементы должны быть отсортированы, только тогда findFirst может сказать, кто действительно первый.

    Просто обратите внимание, что эта оптимизация работает только для естественно отсортированных потоков. Например:

    // prints too much you say?
    Stream.of(new User(30), new User(25), new User(34))
                .peek(x -> System.out.println("1 : before I call first sorted"))
                .sorted(Comparator.comparing(User::age))
                .peek(x -> System.out.println("2 : before I call second sorted"))
                .sorted(Comparator.comparing(User::age))
                .findFirst();
    

    где (для краткости):

    record User(int age) { }
    
    24.05.2021
  • Просто обратите внимание, что эта оптимизация работает только для естественно отсортированных потоков, я полагаю, что это имеет смысл, логический флаг SORTED отслеживает только один тип «отсортированных», и было бы очень сложно отслеживать другие типы (вам понадобится что-то вроде Map<Comparator, Boolean> отслеживать их. 25.05.2021
  • Возможно, имеет отношение к деталям реализации, но если бы характеристики потока были детерминированными для выполнения No-Op, нельзя ли было бы использовать такую ​​оптимизацию в IntStream.range(0, 4).peek(System.out::println).max(); // outputs 0,1,2,3? В отсортированном списке выполнение максимума и минимума должно быть O(1) правильно? 25.05.2021
  • @ Наман, да, я помню, несколько раз об этом говорили. Просто это еще не реализовано. 25.05.2021
  • Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..