Мой топ фишек, ради которых хочется перейти на Java 16 (не язык, только JVM и API). То есть то, что появилось в Java 12-16:
• Stream.toList()
• Collectors.teeing()
• String.formatted()
• Аннотация java.io.Serial
• Lookup.defineHiddenClass() (но не уверен, что смогу найти реальное применение)
• JEP 341: Default CDS Archives (ну типа, всё быстрее будет стартовать)
• JEP 346: Promptly Return Unused Committed Memory from G1 (приложение будет кушать меньше памяти)
• JEP 358: Helpful NullPointerExceptions
• Field layout computation overhaul (более компактные объекты). Ну и вообще подобного рода мелкие оптимизации, которые в целом делают JVM чуть-чуть быстрее.
А ради чего вы хотите перейти с Java 11 на более новую версию?
• Stream.toList()
• Collectors.teeing()
• String.formatted()
• Аннотация java.io.Serial
• Lookup.defineHiddenClass() (но не уверен, что смогу найти реальное применение)
• JEP 341: Default CDS Archives (ну типа, всё быстрее будет стартовать)
• JEP 346: Promptly Return Unused Committed Memory from G1 (приложение будет кушать меньше памяти)
• JEP 358: Helpful NullPointerExceptions
• Field layout computation overhaul (более компактные объекты). Ну и вообще подобного рода мелкие оптимизации, которые в целом делают JVM чуть-чуть быстрее.
А ради чего вы хотите перейти с Java 11 на более новую версию?
Тут в соседнем треде бурление по поводу логирования в Java. А какие библиотеки используются у вас для логирования?
Final Results
3%
java.util.logging
5%
Log4J (первая, древняя версия)
27%
Log4J 2
75%
SLF4J / Logback
1%
Apache Commons Logging
0%
tinylog
0%
System.Logger (JEP 264, Java 9+)
7%
Простой println
3%
Другое (напишу в комментариях)
В общем, что и требовалось доказать. Никита наехал на джавистов, что у нас чуть ли не каждый день создаётся по новой библиотеке логирования. А в реальности у нас по факту всего лишь две библиотеки, которыми пользуется подавляющее большинство: SLF4J и Log4J 2. Остальные библиотеки либо уже ископаемые и давно мертвы и забыты (Commons Logging, Log4J 1, JUL), либо слишком маргинальные (tinylog). Так что картина во все не настолько плохая, как это может показаться по началу.
Кстати, я уже далеко не первый раз слышу набросы по поводу логирования Java со стороны всяких людей. "Да зачем у вас так всё сложно", "Зачем вы переизобретаете каждый раз то же самое" — постоянно я вижу то в Твиттере, то где-то ещё. А ответ заключается в том, что у нас: а) несложно и б) немного. Библиотеки просто отделяют интерфейс от реализации. В чём здесь сложность? И никакой горы библиотек нет. В опросе я вспомнил лишь 6 библиотек. Это за всё время существования Джавы (26 лет!).
Кстати, я уже далеко не первый раз слышу набросы по поводу логирования Java со стороны всяких людей. "Да зачем у вас так всё сложно", "Зачем вы переизобретаете каждый раз то же самое" — постоянно я вижу то в Твиттере, то где-то ещё. А ответ заключается в том, что у нас: а) несложно и б) немного. Библиотеки просто отделяют интерфейс от реализации. В чём здесь сложность? И никакой горы библиотек нет. В опросе я вспомнил лишь 6 библиотек. Это за всё время существования Джавы (26 лет!).
Такой код точно компилируется в Java 16, потому что стали разрешены статические декларации во внутренних классах. А скомпилируется ли в Java 15?
Final Results
29%
Не скомпилируется. Ошибка в обеих константах X и Y.
35%
Не скомпилируется. Ошибка только в константе Y. С X всё ОК.
36%
Скомпилируется.
Кто-нибудь может объяснить, почему в Идее отсутствуют кнопки разворачивания окон на весь экран? Неужели никого это не напрягает? 🤔
#IntelliJIDEA
#IntelliJIDEA
Универсальные дженерики уже попали в репозиторий Valhalla. И там стоит релиз Java 18! Естественно, preview. Значит по логике мы сможем писать
#Java18 #Java20
List<int> в Java 20. Поскорей бы.#Java18 #Java20
GitHub
universal type variables: initial prototype · openjdk/valhalla@3e896fa
Reviewed-by: mcimadamore
Где-то с год назад я делал разбор поста из одного популярного Telegram-канала по Java, где объяснил, почему автор неправ. Решил вот опять заглянуть туда. Ничего не изменилось: автор (точнее авторка) продолжает сбивать с толку своих читателей и вдалбливать в голову совершенно вредные и, в данном случае, неправильные вещи. А количество подписчиков с того момента увеличилось на две тысячи, так что стало всё только хуже.
Перед началом чтения моего разбора поста можете прочитать оригинальный пост, хотя это необязательно (и может быть даже опасно 🤡).
Итак, автор начинает с двух тестов. В первом приводится кусок кода и спрашивается, сколько элементов он выведет:
1. for выведет 2 элемента, forEach выведет 2
2. for выведет 2 элемента, forEach выведет 3
3. for выведет 3 элемента, forEach выведет 2
4. for выведет 3 элемента, forEach выведет 3
Комбинации 2 и 4, например, можно вполне себе легко воспроизвести: на Java 11.0.12 for выведет 20, 30, а forEach 20, 40, 30. Но если взять другие числа, например, 10, 20, 30 вместо 20, 30, 40, то оба выведут одно и то же: 20, 10, 30.
Таким образом, автор, пытаясь объяснить различия между устройством for и forEach, выбрала крайне неудачный (и некорректный пример).
Теперь давайте перейдём к самому посту. Пойдём прямо по тексту:
for использует для обхода итератор, а forEach - траверс.
Не очень понятно, что автор хочет нам этим сказать. И for и forEach оба делают обход. Правильнее было бы сказать: for использует pull-based обход, а forEach – push-based. Отсюда и возможные различия в поведении.
Сразу скажу: всё это имеет смысл только для обхода многопоточных структур данных
Не только. Различия между for и forEach порой существенны и для однопоточных структур. Например, те же стримы могут в некоторых случаях вести себя немного по-разному, если использовать iterator вместо forEach. Кроме того, forEach работает, как правило, быстрее, так как push-based реализация чаще намного проще и эффективнее. Посмотрите, например, как просто реализован ArrayList.forEach и насколько сложнее ArrayList.Itr.
Почему нельзя использовать траверс по умолчанию?
— Итератор проще и работает быстрее, а условия для пропуска элемента при обходе встречаются редко.
Не проще и не быстрее (см. выше). Итератор может быть либо медленнее и сложнее, либо как минимум одинаков по скорости и сложности с forEach (ну если говорить о разумных реализациях, можно конечно специально сделать forEach медленнее).
Перед началом чтения моего разбора поста можете прочитать оригинальный пост, хотя это необязательно (и может быть даже опасно 🤡).
Итак, автор начинает с двух тестов. В первом приводится кусок кода и спрашивается, сколько элементов он выведет:
Map<Integer, Integer> map = new ConcurrentHashMap<>();Во втором тесте цикл for заменён на метод forEach и задаётся тот же самый вопрос:
map.put(20, 0);
map.put(30, 0);
for (Integer key : map.keySet()) {
System.out.println(key);
map.put(40, 0);
}
map.keySet().forEach(key -> {
System.out.println(key);
map.put(40, 0);
});
Без чтения дальнейшего поста уже стоило бы напрячься. Во-первых, модифицировать мапу во время итерации в этом же самом потоке уже немного странно. Ну хорошо, бывает, что иногда приводится не очень практичный пример чисто ради педагогических целей. Есть ли какая-то педагогическая ценность в данном вопросе? Я бы сказал, да, если бы среди ответов был вариант: «чувак, поведение не определено и может произойти что угодно». Ведь для ConcurrentHashMap гарантируется только отсутствие вылета ConcurrentModificationException, а будет ли там подхватываться или не подхватываться элемент – это уже деталь реализации. Среди ответов же есть только два варианта «выведется 2 элемента» и «выведется 3 элемента». На самом деле контракт нам про 2 или 3 сказать ничего не может. Гипотетически может произойти любая комбинация:1. for выведет 2 элемента, forEach выведет 2
2. for выведет 2 элемента, forEach выведет 3
3. for выведет 3 элемента, forEach выведет 2
4. for выведет 3 элемента, forEach выведет 3
Комбинации 2 и 4, например, можно вполне себе легко воспроизвести: на Java 11.0.12 for выведет 20, 30, а forEach 20, 40, 30. Но если взять другие числа, например, 10, 20, 30 вместо 20, 30, 40, то оба выведут одно и то же: 20, 10, 30.
Таким образом, автор, пытаясь объяснить различия между устройством for и forEach, выбрала крайне неудачный (и некорректный пример).
Теперь давайте перейдём к самому посту. Пойдём прямо по тексту:
for использует для обхода итератор, а forEach - траверс.
Не очень понятно, что автор хочет нам этим сказать. И for и forEach оба делают обход. Правильнее было бы сказать: for использует pull-based обход, а forEach – push-based. Отсюда и возможные различия в поведении.
Сразу скажу: всё это имеет смысл только для обхода многопоточных структур данных
Не только. Различия между for и forEach порой существенны и для однопоточных структур. Например, те же стримы могут в некоторых случаях вести себя немного по-разному, если использовать iterator вместо forEach. Кроме того, forEach работает, как правило, быстрее, так как push-based реализация чаще намного проще и эффективнее. Посмотрите, например, как просто реализован ArrayList.forEach и насколько сложнее ArrayList.Itr.
Почему нельзя использовать траверс по умолчанию?
— Итератор проще и работает быстрее, а условия для пропуска элемента при обходе встречаются редко.
Не проще и не быстрее (см. выше). Итератор может быть либо медленнее и сложнее, либо как минимум одинаков по скорости и сложности с forEach (ну если говорить о разумных реализациях, можно конечно специально сделать forEach медленнее).
Итого: при выводе элементов ConcurrentHashMap через for и forEach используются разные алгоритмы обхода, поэтому результат вывода тоже разный.
Как раз-таки для ConcurrentHashMap используется один и тот же алгоритм обхода. Это тот случай, когда for и forEach совпадают. forEach использует вложенный класс Traverser, а for – класс BaseIterator, который наследуется от Traverser. А различия в поведении (for выводит 20, 30, а forEach 20, 40, 30) наблюдаются из-за того, что Traverser более ленив, чем BaseIterator. BaseIterator следующий элемент вычисляет раньше, чем он реально нужен.
Для однопоточных коллекций между for и forEach нет никакой разницы, в обоих случаях используется итератор.
Нет, разница между ними есть. И нет, в обоих случаях не используется итератор. Да, реализация forEach в Iterable по умолчанию делегирует обход к итератору. Но почти все коллекции эту реализацию переопределяют на более эффективную.
Как раз-таки для ConcurrentHashMap используется один и тот же алгоритм обхода. Это тот случай, когда for и forEach совпадают. forEach использует вложенный класс Traverser, а for – класс BaseIterator, который наследуется от Traverser. А различия в поведении (for выводит 20, 30, а forEach 20, 40, 30) наблюдаются из-за того, что Traverser более ленив, чем BaseIterator. BaseIterator следующий элемент вычисляет раньше, чем он реально нужен.
Для однопоточных коллекций между for и forEach нет никакой разницы, в обоих случаях используется итератор.
Нет, разница между ними есть. И нет, в обоих случаях не используется итератор. Да, реализация forEach в Iterable по умолчанию делегирует обход к итератору. Но почти все коллекции эту реализацию переопределяют на более эффективную.
До выхода следующей LTS-версии Java остаётся 3 дня. Планируете переходить?
Final Results
31%
Да, после выхода планируем перейти в ближайшее время
31%
Нам и на Java 11 хорошо
31%
Мы застряли на 8
3%
Зачем слезать с Java 7? Работает - не трогай
4%
Да мы вообще на 18 уже в продакшене
Forwarded from Java Memes → АйТи мемес
Пора сделать выбор будущего айти
Anonymous Poll
22%
КПРФ - Котлин программисты РФ
12%
ЗЕЛЁНЫЕ - Go
10%
ЛДПР - Либерально-демократический Питон России
3%
НОВЫЕ ЛЮДИ (нет) - PHP
32%
ЕДИНАЯ ДЖАВА (C# сюда же)
6%
СПРАВЕДЛИВЫЙ TYPE SCRIPT
4%
ЯБЛОКО - Swift
5%
ПАРТИЯ РАСТА
5%
ПАРТИЯ ПЕНСИОНЕРОВ - Cobol, Fortan, C
Классный вводный доклад про арифметику с плавающей точкой. Не поленитесь и потратьте всего полчаса на просмотр. Гарантирую, что время будет потрачено не зря. Я вот, например, узнал, как вообще на английском читается аббревиатура IEEE 754 😅. Оказывается, её произносят как "I triple-E seven fifty-four".
Ну это не самое полезное знание, конечно. Куда важнее понять, что в double числа дискретные и их множество конечно, в то время как вещественные числа непрерывны и их бесконечное количество. Это значит, double аппроксимирует вещественные числа, т.е. каждое вещественное число конвертируется в double путём нахождения ближайшего. Например, числа 0.1 в double нет, а ближайшее к нему это 0.1000000000000000055511151231257827021181583404541015625. Но почему тогда Double.toString(0.1) возвращает "0.1", а не "0.1000000000000000055511151231257827021181583404541015625"?
А ещё из-за такой аппроксимации нарушаются многие аксиомы. Не работают ассоциативность сложения, ассоциативность умножения, дистрибутивность и т.д. Например:
В общем, про всё это можно узнать из лекции.
#IEEE754 #double
Ну это не самое полезное знание, конечно. Куда важнее понять, что в double числа дискретные и их множество конечно, в то время как вещественные числа непрерывны и их бесконечное количество. Это значит, double аппроксимирует вещественные числа, т.е. каждое вещественное число конвертируется в double путём нахождения ближайшего. Например, числа 0.1 в double нет, а ближайшее к нему это 0.1000000000000000055511151231257827021181583404541015625. Но почему тогда Double.toString(0.1) возвращает "0.1", а не "0.1000000000000000055511151231257827021181583404541015625"?
А ещё из-за такой аппроксимации нарушаются многие аксиомы. Не работают ассоциативность сложения, ассоциативность умножения, дистрибутивность и т.д. Например:
> 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1Но:
$1 ==> 0.9999999999999999
> (0.1+0.1+0.1+0.1)+(0.1+0.1+0.1+0.1)+0.1+0.1(Это кстати отличный пример, демонстрирующий, почему double ни в коем случае нельзя использовать для хранения денег)
$2 ==> 1.0
В общем, про всё это можно узнать из лекции.
#IEEE754 #double