microJUG – Telegram
microJUG
981 subscribers
155 photos
1 video
2 files
237 links
Мысли о Java.
Основной канал: @miniJUG
Буст: https://news.1rj.ru/str/microJUG?boost
Чат: https://news.1rj.ru/str/micro_JUG
Таблица JEP'ов: https://minijug.org/jeps.html
Download Telegram
В общем, что и требовалось доказать. Никита наехал на джавистов, что у нас чуть ли не каждый день создаётся по новой библиотеке логирования. А в реальности у нас по факту всего лишь две библиотеки, которыми пользуется подавляющее большинство: SLF4J и Log4J 2. Остальные библиотеки либо уже ископаемые и давно мертвы и забыты (Commons Logging, Log4J 1, JUL), либо слишком маргинальные (tinylog). Так что картина во все не настолько плохая, как это может показаться по началу.

Кстати, я уже далеко не первый раз слышу набросы по поводу логирования Java со стороны всяких людей. "Да зачем у вас так всё сложно", "Зачем вы переизобретаете каждый раз то же самое" — постоянно я вижу то в Твиттере, то где-то ещё. А ответ заключается в том, что у нас: а) несложно и б) немного. Библиотеки просто отделяют интерфейс от реализации. В чём здесь сложность? И никакой горы библиотек нет. В опросе я вспомнил лишь 6 библиотек. Это за всё время существования Джавы (26 лет!).
Всё-таки обратная совместимость в Java это сила! Вот так работаешь с проектом, а там зависимость, скомпилированная под Java 1.2. Джава Один Два, 1998 год! И ничего, всё работает, как часы.
Quiz Time! 👇
Такой код точно компилируется в Java 16, потому что стали разрешены статические декларации во внутренних классах. А скомпилируется ли в Java 15?
Final Results
29%
Не скомпилируется. Ошибка в обеих константах X и Y.
35%
Не скомпилируется. Ошибка только в константе Y. С X всё ОК.
36%
Скомпилируется.
Кто-нибудь может объяснить, почему в Идее отсутствуют кнопки разворачивания окон на весь экран? Неужели никого это не напрягает? 🤔
#IntelliJIDEA
Что объединяет все эти примеры кода?
Универсальные дженерики уже попали в репозиторий Valhalla. И там стоит релиз Java 18! Естественно, preview. Значит по логике мы сможем писать List<int> в Java 20. Поскорей бы.
#Java18 #Java20
Где-то с год назад я делал разбор поста из одного популярного Telegram-канала по Java, где объяснил, почему автор неправ. Решил вот опять заглянуть туда. Ничего не изменилось: автор (точнее авторка) продолжает сбивать с толку своих читателей и вдалбливать в голову совершенно вредные и, в данном случае, неправильные вещи. А количество подписчиков с того момента увеличилось на две тысячи, так что стало всё только хуже.

Перед началом чтения моего разбора поста можете прочитать оригинальный пост, хотя это необязательно (и может быть даже опасно 🤡).

Итак, автор начинает с двух тестов. В первом приводится кусок кода и спрашивается, сколько элементов он выведет:

Map<Integer, Integer> map = new ConcurrentHashMap<>();
map.put(20, 0);
map.put(30, 0);

for (Integer key : map.keySet()) {
System.out.println(key);
map.put(40, 0);
}

Во втором тесте цикл for заменён на метод forEach и задаётся тот же самый вопрос:

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 по умолчанию делегирует обход к итератору. Но почти все коллекции эту реализацию переопределяют на более эффективную.
Квиз вам на ночь
Марк Рейнолд объявил, что LTS-релизы будут выходить раз в 2 года, а не в 3.
#java21 #lts
Классный вводный доклад про арифметику с плавающей точкой. Не поленитесь и потратьте всего полчаса на просмотр. Гарантирую, что время будет потрачено не зря. Я вот, например, узнал, как вообще на английском читается аббревиатура IEEE 754 😅. Оказывается, её произносят как "I triple-E seven fifty-four".

Ну это не самое полезное знание, конечно. Куда важнее понять, что в 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
$2 ==> 1.0

(Это кстати отличный пример, демонстрирующий, почему double ни в коем случае нельзя использовать для хранения денег)

В общем, про всё это можно узнать из лекции.

#IEEE754 #double
Думаю, написать кусок кода на Java через стрим или цикл
#юмор