Комментарии 41
Аналог Arrays.asList() для Set не нужен, всегда можно передать результат Arrays.asList в конструктор HashSet. Чаще всего такая инициализация востребована в статических константах, а значит лишняя аллокация не имеет значения.
Что касается Map-а, в java8 использую комбинацию Stream.of с SimpleImmutableEntry (вот она замена Map.entry) и последующий collect в Map.
Да, вариант более многословный, но стандартные, уменьшающие громоздкость объявления констант заменяются нестандартными методами, так что на killer-фичу, заставляющую обновляться даже близко не подходит.
Кучка функций для замены commons-lang и подобных пакетов — это круто, но пока они появляются точечно, от сторонних пакетов отказаться не получится, а это заметно осложняет переход (зачем учить новое апи, если всё равно требуется использовать стороннюю библиотеку, в которой уже есть куча функций, которыми уже привык пользоваться). Конечно, появление этих функций в стандартной джаве — это круто, но почему это происходит так избирательно?
Что касается Predicate.not, в java8 есть Predicate.negate(), правда, он требует использования вспомогательной функции, либо явного каста, иначе компилятор не понимает, что это предикат (это, вроде, должны были починить в одной из более поздних версий). Да, Predicate.not — удобная штука, но на killer-фичу не тянет даже близко.
Аналог Arrays.asList() для Set не нужен, всегда можно передать результат Arrays.asList в конструктор HashSet. Чаще всего такая инициализация востребована в статических константах, а значит лишняя аллокация не имеет значения.
Ну то есть вы предлагаете писать так?
Set<String> set = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("a", "b", "c")));
Очень удобно, ничего не скажешь.
так что на killer-фичу, заставляющую обновляться даже близко не подходит
Где я утверждал, что это киллер-фича? Вы взяли из списка наименее востребованные функции, и сделали вывод, что мигрировать не нужно. А остальные функции проигнорировали. Что скажете по поводу ProcessHandle, takeWhile, Cleaner? Почему вы делаете суждения исходя из востребованности каждой отдельной функции, а не их в совокупности? А почему вы проигнорировали тот факт, что это лишь первая часть? Будут ещё статьи и будет ещё много API.
Очень удобно, ничего не скажешь.
Писать так каждый раз не нужно, достаточно один раз написать статическую функцию-обёртку для кода, либо воспользоваться библиотекой, где это уже есть. В любом случае, для существующего проекта эти новые функции не принесут выгоды при переезде на java9+, так как наверняка уже в проекте есть решение проблем, которые решают такие методы.
Про takeWhile не писал, так как согласен что это killer-фича и её бэкпортирование нетривиально, однако, честно говоря, ни в одном из проектов, с которыми работал на java8, не встречал бесконечных стримов, а прерывание обработки стрима осуществлял посредством runtimeexception'а (в случае реальной ошибки) или просто делал filter с последующим взятием первой записи. Это, конечно, зависит от флагов стрима, но при однопоточной обработке и создании стримов из коллекций, новый элемент не получается до тех пор, пока предыдущий не прошел цепочку операций над стримами до момента отфильтровывания или другой операции, требующей получения следующего элемента (это наблюдение сделанное на основании последовательности срабатывания брейкпоинтов при отладке).
Что касается ProcessHandle и Cleaner, то первый нужен для специфических целей и его появление обязано расширению границ применения java вообще, а к последнему я отношусь как к обычной смене одного апи на другое (лично мне пришлось писать финализатор лишь единожды). Что касается гарантий закрытия у Closable, появление достаточно умных ide, которые теперь ругаются на отсутствие вызова close у Closable-объекта, делает данную защиту от забытого close менее острой чем раньше.
Что скажете по поводу ProcessHandle, takeWhile, Cleaner?
В моей практике они были нужные почти никогда. Точнее ProcessHandle — вообще ни разу, takeWhile — потенциально пару раз мог пригодиться, а Cleaner — чистейший code smells.
"Я это не использую, значит оно не нужно"
Оно не нужно в вебе, оно мало кому нужно в десктопных приложениях. Я не говорю, что реализация этого API плохо, я отвечаю на ваш домысел, что это "востребованные функции". Но дело в том, что они востребованы в узком списке специфических потребностей, а большинству — не нужны. Т.е. они из категории "есть и хорошо", а не из категории "дайте срочно мне!". Вот стримы и nio отвечали потребностям более широкой аудитории, поэтому на java 7/8 переход был более массовый, чем переход на более современные. Ну вот поставил я себе 11-ую JDK и не пользуюсь её фичами (кошмар какой?!)...
Stream записей является почти бесконечным, поэтому filter() использовать не получитсяа почему?
Objects.requireNonNullElse()
Чего только не придумают, лишь бы не добавлять в язык elvis operator. Неужели никому из тех кто работал с этим JEP не показалось, что эта конструкция как минимум громоздкая и неуклюжая? Кто-нибудь в курсе, почему изменений непосредственно языка боятся как огня? Я ещё могу принять объяснение, которое приводили против литералов коллекций — такая конструкция гвоздями прибила бы язык к стандартной библиотеке, потому что нужно было бы специфицировать, какой тип коллекции должен возвращать литерал (хотя наличие autoboxing'а, Iterable и AutoClosable уменьшают вес этого аргумента). Но чем помешает прокачка тернарного оператора? Сочетание символов ?:
и так является невалидным, введение его как языковой конструкции не сломает существующий код. Равно как оператор ?.
для null-safe доступа к методам и свойствам. Да, Optional удобен в тех ситуациях, когда нужно явно обозначить, что значение может отсутствовать, но писать Optional.ofNullable() (кстати, почему Optional.of() бросает NPE если на входе null?) каждый раз, когда хочется просто получить вложенное свойство — неудобно.
Не поймите меня неправильно, я не хейтер, я сам пишу на Java и радуюсь улучшению API стандартной библиотеки. Но requireNonNullElse — это какой-то сюр.
кстати, почему Optional.of() бросает NPE если на входе null?
Потому что Optional<T>
сделан как обертка над нуллабельным полем типа T
, соответственно отличить пустой Optional
от Optional.of(null)
нет возможности. Наверное в теории можно было бы сделать так чтобы Optional.of(null)
возвращал пустой Optional
, но вероятно для дополнительной null-безопасности решили этого не делать.
Optional.ofNullable()
это не мешает. Всё отличие между of()
и ofNullable()
заключается в простой проверке на null. Если Optional
делали как средство борьбы с NPE, то почему более коротким (читай — дефолтным) сделали метод, который ожидает не-null аргумент? Неужели пытались производительность улучшить, убрав один if?
В погоне за модой фукнкионального стиля люди начинают использовать
requireNonNullElse() или orElse()
и код который был в ветви else, которая раньше не выполнялась, теперь выполняется всегда, причем до основного if (который теперь стал filter или anyMatch).
Optional.ofNullable() тоже зло.
Люди пихают его везде, просто по привычке чтобы не парится с нуллами.
В итоге баги доходят до UI и живут пока юзеры не пожалуются что у них тут что-то пустое место вместо нужных им цифр.
Единственная весомая причина не переходить на котел — прибитый гвоздями к легаси энтерпрайз. Все-таки соединение котел-джава не совсем стабильное и бесшовное, и использовать существующие джава-библиотеки может быть не так и просто.
В остальном же, любой, кто видел в жизни кроме джавы вообще хоть что-то, выберет котел без раздумий.
// Java 9+
IntStream
.iterate(1, i -> i < 100, i -> i * 2)
.forEach(System.out::println);
Это вообще, садизм. Как надо было заоптимизировать цикл for, что бы нативный инкремент регистра процессора стал медленнее stream.
/ Java 8
for (int i = 1; i < 100; i *= 2) {
System.out.println(i);
}
Переменная i, это не Java Object, а вполне себе, достаточная переменная, внутри стека. Посмотрим на конструкцию
.iterate(1, i -> i < 100, i -> i * 2)
Тут, как минимум, 2 замыкания. Как вы думаете, что быстрее выполнится, просто операция или замыкание, выполняющее эту операцию?
Если уж приводить пример, надо показывать действительно то, что не будет выглядеть на столько бессмысленным.
Статья-то тут при чем? Перечитайте свою изначальную фразу:
Как надо было заоптимизировать цикл for, что бы нативный инкремент регистра процессора стал медленнее stream.
Что тут медленнее чего, кого оптимизировали — вообще непонятно. Не в статье — а у вас. Сейчас со второго раза другими словами — уже норм, но сначала было как-то мрачно.
Если уж приводить пример, надо показывать действительно то, что не будет выглядеть на столько бессмысленным.
Ну ёлы-палы, добавьте пару операций к стриму, и он сразу же станет смысленным. Ну, так, например:
System.out.println(IntStream
.iterate(1, i -> i < 100, i -> i * 2)
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "))); // 1, 2, 4, 8, 16, 32, 64
А с циклом
for
то же самое будет уже сильно сложнее.System.out.println("1, 2, 4, 8, 16, 32, 64");
Я всё равно буду стоять на своём, что надо показывать примеры эффективного использования разных конструкций. На практике, бывает настолько не эффективное использование. Например, старый добрый интерфейс итератора. Итератор удаляет пустые строки.
List<String> list; // размер списка, порядка 20-30 записей
for(Iterator<String> it; it.hasHext(); ) {
String val = it.next();
if (val == null || val.isEmpty()) it.remove();
}
return list;
Превращается в поток, фильтр и преобразование в новый список. Красиво — да. Эффективно — нет. Если усложнять задачу, возможно, будет эффективней переход на потоки.
Возвращаясь к оператору for, методу iterate не всегда применим. Скажем, нам нужен бесконечный цикл и выход по некоторому условию. В замыкании нет break, для прерывания цикла и нет return для остановки потока. Или всё это есть?
List<String>Arrays.asList("bmp", "jpg", "png", "gif");
Попытка добавить в такой список что-либо приведет к java.lang.UnsupportedOperationException
API, ради которых наконец-то стоит обновиться с Java 8. Часть 1