Как стать автором
Обновить

Комментарии 9

>Если есть идеи выраженные в коде, прошу их pull-реквестить :)
Не очень понятно, какой стимул будет у тех, кто контрибьютит в ваш проект.

У вас же идея была в самообучении, но все самое основное вы уже реализовали. А практическое использование такого проекта — штука сомнительная, хотя бы потому, что уже есть довольно широко известные похожие проекты. Грубо говоря — стримы без parallel, это же просто коллекции, у которых есть map/flatMap/etc. Ну т.е. как минимум, есть скала, груви, vavr в виде библиотеки, где все очень похоже. Чтобы развивать такой проект, должен быть какой-то еще смысл в нем, мне кажется.

Самообучение это конечно очень важная вещь, но статья полна неточностей, заблуждений и незнания матчасти. К примеру про Optional

Optional у меня может быть null`ом. Да может, и это его логичное на мой взгляд применение. Не нашли значение — опционал == null, нашли значение, опционал его содержит (даже если оно null), иначе какая польза от этого опционала? Да никакой! 

Советую посмотреть про Optional у Stuart Marks https://www.youtube.com/watch?v=fBYhtvY19xA&ab_channel=Devoxx и сразу многое прояснится.

И такого у вас много...

Кстати для "гонок" производительности стоит попробовать написать микробенчмарк с помощью JMH. Благо статей по нему много .

P.S. Статью стоит перечитать и переписать, что должно принести не меньшую пользу.

Советую посмотреть про Optional у Stuart Marks

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

И такого у вас много...

приведите еще примеры

Кстати для "гонок" производительности стоит попробовать написать микробенчмарк с помощью JMH.

согласен, но для данной статьи не критично, так как тест производительности тут дается лишь "постольку-поскольку" вкратце. И если копать в сторону производительности то нужно начинать не с теста, НО хорошим тестом заканчивать :)

P.S. Статью стоит перечитать и переписать, что должно принести не меньшую пользу.

какие еще неточности обнаружились кроме указанных выше?

P.S. за них - благодарность, особенно с Optional. добавил UPD1 с сохранением своего "ляпа"

Советую посмотреть про Optional у Stuart Marks

Посмотрел. Нужные рекомендации, с которыми я уже был знаком. Но напрашивается один нюанс...

В этом видео Стюарт Маркс показывает то, как введение Optional в Java 8 позволяет избежать лишних проверок return'ов на null. Но он упустил один важный баг нюанс связанный с Optional и Stream.

Как я писал выше в этой публикации, методы возвращающие Optional (а это: findFirst(), findAny(), min(), max(), reduce() ) не могут работать с null-значениями. Как то странно получается... На пустом стриме/коллекции они вернут Optional.empty(), а значение (хоть оно и null, ! но это значение !), приведет к трудноуловимому NPE. Конечно это баг особенность Stream, но и "вина" Optional тут имеется. Ведь если бы Optional не существовало, то findFirst() просто вернул бы null как first значение. В нынешнем же варианте (Java 8 и 11), он вообще ничего не вернет, а упадет с NPE. Поэтому тут мы теряем больше, чем приобретаем, так как теперь, чтобы гарантировать предсказуемую работу этих методов мы должны проверять на наличие null не возвращаемое значение а входные аргументы... !входные аргументы, Карл! (что очевидно еще хуже)

Примеры кода, который будет падать, если personList будет содержать null:

№1:

String getNameOfMinimalPerson(List<Person> personList) {
  return personList.stream()
    .min(Comparator.nullsFirst(Comparator.comparingInt(Person::getId))) //#2
    .orElse("Empty persons list, please fill it.");
}

№2:

    String getNameOfRandomPerson(List<Person> personList) { //<- throws an exception
        int rnd = ThreadLocalRandom.current().nextInt(personList.size());

        return Streamer.from(personList)
                .skip(rnd) 
                .findFirst() //упадет если "rnd" окажется равным индексу null значения в списке
                .map(Person::getName)
                .orElse("No persons! Please fill it and try again");
    }

Ну и на последок...

Ироничным выглядит тот факт, что код, который Стюарт приводит в качестве примера безопасного использования Optional для предотвращения NPE (время 9:38) лишен проверки на null там где она нужна. В итоге код подвержен NPE прямо "из коробки", даже без малейших его изменений:

String customerNameById(List<Customer> custList, int custId) {
  Optional<Customer> opt =
    custList.stream()
    	.filter(c -> c.getId() == custId) //нет проверки "c" на null
      .findFirst(); //#1
    
  return opt.isPresent() ? opt.get().getName()
}

Если в custList'e будет хотя бы один null упадет метод filter() который не проверяет c на null

Вот, как то так...

Ироничным выглядит тот факт, что код, который Стюарт приводит в качестве примера безопасного использования Optional для предотвращения NPE (время 9:38) лишен проверки на null там где она нужна. В итоге код подвержен NPE прямо "из коробки", даже без малейших его изменений

Думаю, что код, который Стюарт привёл как пример, написан с допущением, что коллекция не содержит null, так как мы не видим контекста в котором будет вызываться этот метод. А так то можно и вместо custList передать null и получить NPE.

На пустом стриме/коллекции они вернут Optional.empty(), а значение (хоть оно и null, ! но это значение !), приведет к трудноуловимому NPE. Конечно это баг особенность Stream, но и "вина" Optional тут имеется. Ведь если бы Optional не существовало, то findFirst() просто вернул бы null как first значение.

Так как findFirst возвращает Optional, то можно написать вот такой код (пример синтетический):

        var result = players.stream()
                .filter(player -> player.getScore() > 100)
                .findFirst()
                .map(Player::getData)
                .map(History::filterData)
                .map(History::processData)
                .orElseGet(Collections::emptyList);

В случае если бы findFirst возвращал null, то код оброс бы проверками c if и красивой цепочки не получилось. Считаем, что коллекция players не содержит null значений, но можно конечно сделать фильтрацию по ним, если уж режет глаз.

Вы плохо прочитали/поняли то, что я имел в виду:

Так как findFirst возвращает Optional, то можно написать вот такой код (пример синтетический):

В том то и дело, findFirst() НЕ возвращает Optional если value == null. В этом случае он падает с NPE... Порождение происходит вызовом of(), вместо ofNullable(). Поэтому, код который вы привели так же не будет работать, до ваших map'ов дело не дойдет. Падение будет в findFirst(), никакого Optional'a создано не будет

Я не зря написал про допущение, что коллекции не содержат null, потому как в обоих примерах, если будет null, то будет NPE и я с этим фактом не спорю, но я пытался объяснить почему findFirst не возвращает null, а возвращает Optional. Это один момент, а второй момент, что в javadoc к методу findFirst написано, что метод может выбросить NPE и это говорит о том, что у создателей API были на то причины. Другой вопрос, что нам такое положение вещей может не нравится и поэтому появляются альтернативы по типу vavr .

да, но это как то странно,,, мы можем быть пустыми (size == 0), но вот null содержать не можем....

наверняка у дизайнеров JDK были какие то причины на это, узнать бы их еще..... а так выглядит очень ляпистым просчетом))

А что по этому поводу думает товарищ @lany?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории