Утренний java puzzle♨️
Проверь себя и узнай правильный ответ в статье через час.
Проверь себя и узнай правильный ответ в статье через час.
Сборщики мусора. Плюсы и минусы G1.
Сборщик мусора(garbage collector) - компонент JVM, который размещает объекты в памяти. Выделяет место под новые и определяет те, которые уже не нужны.
В java есть 5 типов сборщика мусора - Serial Collector, Parallel Collector, Concurrent Mark Sweep (CMS), Garbage-First (G1) и Z Garbage Collector(ZGC). В java 8 вариантом по умолчанию является Parallel GC, в java 9 это G1. ZGC - самый новый, он появился в java 11.
Эффективность GC оценивается по следующим параметрам:
▪️Latency — сколько времени занимает сборка мусора. Измеряется в миллисекундах.
▪️Throughput — как часто она происходит. Пропускная способность считается в процентах времени VM и рассчитывается как обратная величина. Если сборка мусора занимает 5% времени,то throughput равен 95%.
▪️Footprint — сколько ресурсов использует GC. Учитывается количество памяти для корректной работы.
Достичь хороших значений во всех трёх показателях невозможно. Для разных GC в приоритете будут разные параметры, но для каждого доступно множество опций для изменений под требования системы.
Основной принцип работы сборщика мусора — разделение объектов по поколениям.
▫️Недавно созданные объекты относятся к «молодым». Промежуточные объекты, итераторы, локальные переменные - самые вероятные кандидаты на удаление. На них сфокусирована основная часть внимания GC. Ещё они делятся на 2 подгруппы: только что созданные и выжившие после нескольких сборок мусора.
▫️Если объект пережил много циклов работы GC (по умолчанию 15), то он переходит в «старое» поколение и проверяется реже.
Деление на поколения не абстрактное. Если объект переходит в другое поколение, он физически меняет своё место в памяти, и все ссылки на него обновляются.
В ранних версиях сборщиков мусора память делилась на 4 региона:
🟥 Eden — здесь создавались новые объекты.
🟧 Survivor – для переживших несколько сборок мусора.
🟪 Tenured – для долгоживущих объектов.
🟦 Permanent – метаинформация JVM о классах, методах и тд.
Выглядело всё примерно так:
🟥🟥🟥🟥🟥🟥🟧🟧🟧🟪🟪🟦
В G1 область Permanent удалена, а метаинформация перенесена в отдельную часть памяти под названием Metaspace.
Второе изменение — память разделилась на 2000 регионов одного размера, по умолчанию 1МБ. Тип региона может меняться. Для больших объектов регионы объединяются.
Стало как-то так:
🟥🟪🟧🟧🟥🟪🟥🟥🟧🟪
Плюсы и минусы G1:
✅ Выборочная обработка группы регионов – получаем высокий latency.
✅ Предсказуемое время обработки.
✅ Сборка мусора в основном выполняется параллельно с работой приложения.
✅ Нет проблем с фрагментацией.
➖ Затраты на поддержку такой системы чуть снижают throughput.
Целевое значение пропускной способности для G1 - 90%. Для Parallel GC это значение равно 99%.
➖ Плохо работает с heap меньше 6Гб.
Чтобы проанализировать и оптимизировать работу G1, в первую очередь нужно включить логгирование, а уже затем экспериментировать с флажками JVM.
-
Но вообще G1 адаптируется к нагрузке и прекрасно работает со значениями по умолчанию.
Сборщик мусора(garbage collector) - компонент JVM, который размещает объекты в памяти. Выделяет место под новые и определяет те, которые уже не нужны.
В java есть 5 типов сборщика мусора - Serial Collector, Parallel Collector, Concurrent Mark Sweep (CMS), Garbage-First (G1) и Z Garbage Collector(ZGC). В java 8 вариантом по умолчанию является Parallel GC, в java 9 это G1. ZGC - самый новый, он появился в java 11.
Эффективность GC оценивается по следующим параметрам:
▪️Latency — сколько времени занимает сборка мусора. Измеряется в миллисекундах.
▪️Throughput — как часто она происходит. Пропускная способность считается в процентах времени VM и рассчитывается как обратная величина. Если сборка мусора занимает 5% времени,то throughput равен 95%.
▪️Footprint — сколько ресурсов использует GC. Учитывается количество памяти для корректной работы.
Достичь хороших значений во всех трёх показателях невозможно. Для разных GC в приоритете будут разные параметры, но для каждого доступно множество опций для изменений под требования системы.
Основной принцип работы сборщика мусора — разделение объектов по поколениям.
▫️Недавно созданные объекты относятся к «молодым». Промежуточные объекты, итераторы, локальные переменные - самые вероятные кандидаты на удаление. На них сфокусирована основная часть внимания GC. Ещё они делятся на 2 подгруппы: только что созданные и выжившие после нескольких сборок мусора.
▫️Если объект пережил много циклов работы GC (по умолчанию 15), то он переходит в «старое» поколение и проверяется реже.
Деление на поколения не абстрактное. Если объект переходит в другое поколение, он физически меняет своё место в памяти, и все ссылки на него обновляются.
В ранних версиях сборщиков мусора память делилась на 4 региона:
🟥 Eden — здесь создавались новые объекты.
🟧 Survivor – для переживших несколько сборок мусора.
🟪 Tenured – для долгоживущих объектов.
🟦 Permanent – метаинформация JVM о классах, методах и тд.
Выглядело всё примерно так:
🟥🟥🟥🟥🟥🟥🟧🟧🟧🟪🟪🟦
В G1 область Permanent удалена, а метаинформация перенесена в отдельную часть памяти под названием Metaspace.
Второе изменение — память разделилась на 2000 регионов одного размера, по умолчанию 1МБ. Тип региона может меняться. Для больших объектов регионы объединяются.
Стало как-то так:
🟥🟪🟧🟧🟥🟪🟥🟥🟧🟪
Плюсы и минусы G1:
✅ Выборочная обработка группы регионов – получаем высокий latency.
✅ Предсказуемое время обработки.
✅ Сборка мусора в основном выполняется параллельно с работой приложения.
✅ Нет проблем с фрагментацией.
➖ Затраты на поддержку такой системы чуть снижают throughput.
Целевое значение пропускной способности для G1 - 90%. Для Parallel GC это значение равно 99%.
➖ Плохо работает с heap меньше 6Гб.
Чтобы проанализировать и оптимизировать работу G1, в первую очередь нужно включить логгирование, а уже затем экспериментировать с флажками JVM.
-
Xloggc:/path/to/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime Но вообще G1 адаптируется к нагрузке и прекрасно работает со значениями по умолчанию.
IT стендап: неожиданный способ стать сотрудником, за которого бьются компании.
У всех есть коллега, который часто предлагает неожиданные решения. Иногда неподходящие, а иногда настолько изящные, что в голову приходит мысль: "Эх, почему не я это придумал".
Чтобы стать сотрудником, которым втайне восхищаются коллеги, нужно видеть проблемы и решения, смотреть на привычные вещи под разными углами. Это сложный, но очень ценный навык, который можно развивать.
Самый доступный способ — придумывать шутки.
Знаете, как пишется материал для КВН, фильмов, мемчиков и стендапов? Редко это лёгкий и остроумный полёт мысли. Часто этот процесс состоит из нескольких шагов:
1️⃣ Погружение в тему и сбор фактов.
2️⃣ Поиск парадоксов и неожиданных сопоставлений.
3️⃣ Поиск выразительной формы для максимального эффекта.
Эти действия развивают критическое мышление, умение смотреть на вещи с другого ракурса, генерировать, фильтровать и доносить новые идеи. То, что пригодится и на работе, и в жизни.
Первый шаг уже сделан - у вас есть огромный бэкграунд разработки. Настало время идти дальше! Следующий шаг - поиск несоответствий и противоречий.
Посмотрите на привычные вещи со стороны. Есть ли в них что-то нелогичное? Необязательно сразу придумывать шутку, главное — заметить что-нибудь интересное.
Например, вот нескольких идей, вдохновлённых java 8 и 9:
1️⃣ Stream API:
▫️ожидание: название наводит на мысль о работе с потоками данных, компактном описании бизнес-процессов.
▪️реальность1: обработка статичных коллекций без использования циклов.
▪️реальность2: для дебага и обработки исключений легче переписать обратно на циклы.
2️⃣ Parallel Streams:
▫️ожидание: существенный прирост производительности при добавлении .parallel()
▪️реальность: под большой нагрузкой работает в лучшем случае на 20% быстрее, в худшем - на 15% медленнее.
3️⃣ Выход java 9:
▫️ожидание: тренд на модульную архитектуру, новый взгляд на проектирование приложений.
▪️реальность: разработчики в восторге от List.of() и Map.of().
4️⃣ Развитие фичи pattern matching:
▫️ожидание: удобный апи для регулярных выражений.
▪️реальность: проверка и приведение типа объекта пишется в одну строку, а не в две.
5️⃣ Поддержка реактивного программирование в java 9:
▫️ожидание: сверхскорость выполнения кода, новый подход к разработке.
▪️реальность: интерфейсы для паттерна Observer.
Можете развить и довести эти идеи до конца, а можете придумать свои. Сначала это будет казаться сложным, но это навык, и его можно тренировать.
Камон, шутки про java могут быть не только про кофе и многословность😉
У всех есть коллега, который часто предлагает неожиданные решения. Иногда неподходящие, а иногда настолько изящные, что в голову приходит мысль: "Эх, почему не я это придумал".
Чтобы стать сотрудником, которым втайне восхищаются коллеги, нужно видеть проблемы и решения, смотреть на привычные вещи под разными углами. Это сложный, но очень ценный навык, который можно развивать.
Самый доступный способ — придумывать шутки.
Знаете, как пишется материал для КВН, фильмов, мемчиков и стендапов? Редко это лёгкий и остроумный полёт мысли. Часто этот процесс состоит из нескольких шагов:
1️⃣ Погружение в тему и сбор фактов.
2️⃣ Поиск парадоксов и неожиданных сопоставлений.
3️⃣ Поиск выразительной формы для максимального эффекта.
Эти действия развивают критическое мышление, умение смотреть на вещи с другого ракурса, генерировать, фильтровать и доносить новые идеи. То, что пригодится и на работе, и в жизни.
Первый шаг уже сделан - у вас есть огромный бэкграунд разработки. Настало время идти дальше! Следующий шаг - поиск несоответствий и противоречий.
Посмотрите на привычные вещи со стороны. Есть ли в них что-то нелогичное? Необязательно сразу придумывать шутку, главное — заметить что-нибудь интересное.
Например, вот нескольких идей, вдохновлённых java 8 и 9:
1️⃣ Stream API:
▫️ожидание: название наводит на мысль о работе с потоками данных, компактном описании бизнес-процессов.
▪️реальность1: обработка статичных коллекций без использования циклов.
▪️реальность2: для дебага и обработки исключений легче переписать обратно на циклы.
2️⃣ Parallel Streams:
▫️ожидание: существенный прирост производительности при добавлении .parallel()
▪️реальность: под большой нагрузкой работает в лучшем случае на 20% быстрее, в худшем - на 15% медленнее.
3️⃣ Выход java 9:
▫️ожидание: тренд на модульную архитектуру, новый взгляд на проектирование приложений.
▪️реальность: разработчики в восторге от List.of() и Map.of().
4️⃣ Развитие фичи pattern matching:
▫️ожидание: удобный апи для регулярных выражений.
▪️реальность: проверка и приведение типа объекта пишется в одну строку, а не в две.
5️⃣ Поддержка реактивного программирование в java 9:
▫️ожидание: сверхскорость выполнения кода, новый подход к разработке.
▪️реальность: интерфейсы для паттерна Observer.
Можете развить и довести эти идеи до конца, а можете придумать свои. Сначала это будет казаться сложным, но это навык, и его можно тренировать.
Камон, шутки про java могут быть не только про кофе и многословность😉
Сколько времени Вы можете отвечать на вопрос "как создать поток"?
Anonymous Poll
75%
До 5 минут
16%
5-15 минут
9%
15-60 минут
Как создать поток: 7 вариантов ответа.
Выбирайте любой или комбинируйте несколько, ведь это отличный шанс показать широкий кругозор и перевести разговор в интересную Вам сторону.
1️⃣ Синтаксический ответ — ожидаемый.
🅰️ реализовать интерфейс Runnable или Callable и передать новому объекту Thread:
3️⃣ Практический ответ.
Создание потока - ресурсоёмкая операция, поэтому лучше создать пул потоков, которые можно переиспользовать.
Понятие потока в программировании очень размыто, особенно с учётом русского языка. Thread - структура для параллельного выполнения задач с помощью планировщика и потоков ОС.
Поток данных - тоже поток. В случае Stream API это скорее статичная коллекция, но можно рассказать интервьюеру основные методы его создания:
Раз мы заговорили о потоке как потоке данных, то здесь можно свернуть в тему реактивного программирования. Самые популярные библиотеки для java это RxJava2, Project Reactor и Akka Streams. А также интерфейсы Flow API в java 9 и Reactive Streams.
Для примера рассмотрим создание потока в Reactor.
Поток из 0 или 1 элемента называется Mono:
В мире big data есть много фреймворков для обработки потоков данных. Apache Spark Streaming, Storm, Flink, Samza, Kafka Streams - если Вы работали с чем-то из этого списка, то обязательно поделитесь с интервьером этим опытом.
7️⃣ Менеджерский ответ.
Рассмотрим поток с нетехнической точки зрения. Поток - состояние, когда человек полностью включен в свою деятельность, совершенно сконцентрирован на ней и испытывает удовольствие от работы.
Задача руководителя - создать сотрудникам подходящие для этого условия. Можно использовать множество методов: регулярная обратная связь, увеличения прозрачности процессов и т.д.
Ответ на банальный вопрос "как создать поток" может различаться в зависимости от опыта и текущей должности. Вопросы на собеседованиях - лишь стартовая точка для обсуждений, не стесняйтесь повернуть разговор на обсуждение Ваших компетенций и сильных сторон.
Выбирайте любой или комбинируйте несколько, ведь это отличный шанс показать широкий кругозор и перевести разговор в интересную Вам сторону.
1️⃣ Синтаксический ответ — ожидаемый.
🅰️ реализовать интерфейс Runnable или Callable и передать новому объекту Thread:
Runnable action = () -> System.out.println("hey");
new Thread(action).start();
🅱️ расширить класс Thread, переопределить метод run, создать экземпляр этого класса:class MyThread extends Thread {...}
new MyThread().start();
2️⃣ Углублённый ответ.new Thread(...) приведёт к созданию в heap экземпляра класса Thread и сопутствующих объектов —program counter, хранилище thread-local переменных, нескольких стеков, объектов синхронизации и т.д. Вызов метода start() вызывает нативный метод start0(). Через JNI исполнится метод JVM JVM_StartThread, реализованный на C++, который вызывает метод ОС для создания потока. Для Unix это pthread_create.3️⃣ Практический ответ.
Создание потока - ресурсоёмкая операция, поэтому лучше создать пул потоков, которые можно переиспользовать.
ExecutorService executor = Executors.newCachedThreadPool();
Runnable action = () -> System.out.println("hey");
executor.submit(action);
4️⃣ Неожиданный ответ.Понятие потока в программировании очень размыто, особенно с учётом русского языка. Thread - структура для параллельного выполнения задач с помощью планировщика и потоков ОС.
Поток данных - тоже поток. В случае Stream API это скорее статичная коллекция, но можно рассказать интервьюеру основные методы его создания:
list.stream()
Stream.of(...)
Stream.range(...)
Arrays.stream(...)
Stream.generate(...)
5️⃣ Хайповый ответ.Раз мы заговорили о потоке как потоке данных, то здесь можно свернуть в тему реактивного программирования. Самые популярные библиотеки для java это RxJava2, Project Reactor и Akka Streams. А также интерфейсы Flow API в java 9 и Reactive Streams.
Для примера рассмотрим создание потока в Reactor.
Поток из 0 или 1 элемента называется Mono:
Mono.just(1)
Mono.fromCallable(...)
Mono.fromFuture(...)
Mono.fromSupplier(...)
Поток из N элементов называется Flux:Flux.just(1, 2, 3)
Flux.fromIterable(...)
Flux.fromStream(...)
Flux.range(...)
6️⃣ Big data oriented ответ.В мире big data есть много фреймворков для обработки потоков данных. Apache Spark Streaming, Storm, Flink, Samza, Kafka Streams - если Вы работали с чем-то из этого списка, то обязательно поделитесь с интервьером этим опытом.
7️⃣ Менеджерский ответ.
Рассмотрим поток с нетехнической точки зрения. Поток - состояние, когда человек полностью включен в свою деятельность, совершенно сконцентрирован на ней и испытывает удовольствие от работы.
Задача руководителя - создать сотрудникам подходящие для этого условия. Можно использовать множество методов: регулярная обратная связь, увеличения прозрачности процессов и т.д.
Ответ на банальный вопрос "как создать поток" может различаться в зависимости от опыта и текущей должности. Вопросы на собеседованиях - лишь стартовая точка для обсуждений, не стесняйтесь повернуть разговор на обсуждение Ваших компетенций и сильных сторон.
👍5❤2🔥1
Собеседование: план рассказа о прошлом опыте.
Техническое собеседование обычно состоит из трёх частей:
1️⃣ Синтаксические и алгоритмические задачки
2️⃣ Рассказ о прошлом опыте
3️⃣ Вопросы на свободную тему
Первая часть часто проходит по шаблону вопрос-ответ, её цель - проверить бэкграунд и знание языка. Но интервьюера также интересует опыт кандидата, кругозор и навыки общения. Содержательный рассказ о прошлых проектах - это по сути самопрезентация. Она помогает работодателю понять, подходите Вы для вакансии или нет.
Чтобы не тратить время на лишнее, сфокусироваться на главном и ничего не забыть рекомендуем организовать рассказ по следующей схеме:
🔶️Проекты, над которыми Вы работали больше 2 лет назад. 2-3 предложения о теме проекта и основных технологиях.
🔶️ Предпоследний проект — год-полтора назад. Тоже кратко, 4-5 предложений.
🔶️ Последний проект. Это самая актуальная и важная информация, поэтому уделите описанию большую часть времени.
Для описания проекта хорошо подходит шаблон «сверху вниз»:
1️⃣ Цель проекта: решаемые бизнес-задачи.
2️⃣ Общая схема: верхнеуровневая архитектура и основные технологии.
3️⃣ Контекст: компонент, который разрабатывала Ваша команда. Покажите на общей схеме, кратко опишите функции и уточните технологии. Расскажите о составе команды, процессе, по которому работали и с кем ещё взаимодействовали.
4️⃣ Ваша деятельность. Эта часть - кульминация, лучше подготовить её заранее. Кратко расскажите о фичах, которыми Вы занимались. Не углубляйтесь в бизнес-описание, лучше подробнее расскажите о результатах, решённых проблемах, какие подходы и инструменты использовали. Если какие-то из них упоминаются в вакансии — сделайте на них особый упор. Чем больше самопрезентация будет опираться на требования вакансии — тем больше шансов получить оффер.
Такое описание проектов максимально выделит Ваши навыки и достижения. Интервьюер увидит, что Вы:
▪️понимаете задачи бизнеса
▪️видите общую картину и свой вклад в неё
▪️понимаете процессы внутри команды
▪️умеете решать проблемы
▪️ориентированы на результат
Помните, что направление рассказа о предыдущем опыте — не углубляться в технические детали, а показать, что Вы ответственный сотрудник, решаете поставленные задачи и хорошо работаете в команде.
Ведь именно этого ждёт будущий работодатель.
Техническое собеседование обычно состоит из трёх частей:
1️⃣ Синтаксические и алгоритмические задачки
2️⃣ Рассказ о прошлом опыте
3️⃣ Вопросы на свободную тему
Первая часть часто проходит по шаблону вопрос-ответ, её цель - проверить бэкграунд и знание языка. Но интервьюера также интересует опыт кандидата, кругозор и навыки общения. Содержательный рассказ о прошлых проектах - это по сути самопрезентация. Она помогает работодателю понять, подходите Вы для вакансии или нет.
Чтобы не тратить время на лишнее, сфокусироваться на главном и ничего не забыть рекомендуем организовать рассказ по следующей схеме:
🔶️Проекты, над которыми Вы работали больше 2 лет назад. 2-3 предложения о теме проекта и основных технологиях.
🔶️ Предпоследний проект — год-полтора назад. Тоже кратко, 4-5 предложений.
🔶️ Последний проект. Это самая актуальная и важная информация, поэтому уделите описанию большую часть времени.
Для описания проекта хорошо подходит шаблон «сверху вниз»:
1️⃣ Цель проекта: решаемые бизнес-задачи.
2️⃣ Общая схема: верхнеуровневая архитектура и основные технологии.
3️⃣ Контекст: компонент, который разрабатывала Ваша команда. Покажите на общей схеме, кратко опишите функции и уточните технологии. Расскажите о составе команды, процессе, по которому работали и с кем ещё взаимодействовали.
4️⃣ Ваша деятельность. Эта часть - кульминация, лучше подготовить её заранее. Кратко расскажите о фичах, которыми Вы занимались. Не углубляйтесь в бизнес-описание, лучше подробнее расскажите о результатах, решённых проблемах, какие подходы и инструменты использовали. Если какие-то из них упоминаются в вакансии — сделайте на них особый упор. Чем больше самопрезентация будет опираться на требования вакансии — тем больше шансов получить оффер.
Такое описание проектов максимально выделит Ваши навыки и достижения. Интервьюер увидит, что Вы:
▪️понимаете задачи бизнеса
▪️видите общую картину и свой вклад в неё
▪️понимаете процессы внутри команды
▪️умеете решать проблемы
▪️ориентированы на результат
Помните, что направление рассказа о предыдущем опыте — не углубляться в технические детали, а показать, что Вы ответственный сотрудник, решаете поставленные задачи и хорошо работаете в команде.
Ведь именно этого ждёт будущий работодатель.
👍2
Equality: best practices. Или как правильно переопределить метод equals.
Полный список в конце поста.
Базовая реализация в классе
Несмотря на кажущуюся простоту, в реализации
Странное поведение системы ➡️ неделя дебага по всей бизнес-логике ➡️ ошибка в
Простой способ не ошибиться — не переопределять
1️⃣ Для перечислений (
2️⃣ Когда важны сами экземпляры, а не данные внутри них. Пример - классы
3️⃣ Сравнение объектов вообще не предполагается. Пример - класс
4️⃣ В базовом классе уже есть
5️⃣ Класс имеет модификатор доступа
В остальных случаях его лучше переопределить. Во многих проектах при этом используется аннотация
Итак, при реализации
1️⃣ Рефлексивность — экземпляр должен быть равен сам себе.
2️⃣ Консистентность — если экземпляры равны, то без внешних воздействий они должны оставаться равны.
✅ Сравнивать только неизменяемые поля.
Чтобы обезопасить себя от проблем с
Если используете аннотацию
✅
Если в системе находятся объекты базового класса и его наследника нужно чётко понимать, как они будут между собой сравниваться.
Возьмём класс Точка, где равенство проверяется по координатам.
И его наследника — класс ЦветнаяТочка, где сравниваются координаты и цвет.
4️⃣ Транзитивность
✅ Для иерархий классов писать юнит-тесты на симметричность и транзитивность.
✅ Не использовать
🤔 Если в системе используются объекты базового и произвольного типов рассмотрите вариант замены наследования на композицию.
5️⃣ Сравнение существующего объекта с null должно возвращать false.
✅ Добавить отдельную проверку
1️⃣ Не переопределяйте
2️⃣ Используйте в
3️⃣ Для иерархии классов пишите тесты на симметричность и транзитивность.
4️⃣ Для ключей в
5️⃣ Не использовать
6️⃣ При использовании
7️⃣ Кратко проверить два поля на null и равенство можно через
9️⃣ Не забывайте про
Полный список в конце поста.
Базовая реализация в классе
Object проверяет только равенство ссылок - по умолчанию каждый экземпляр класса равен только самому себе. Часто рекомендуют для новых классов переопределять метод equals и проверять равенство по значимым полям.Несмотря на кажущуюся простоту, в реализации
equals есть несколько тонких моментов. Кода в этом методе мало, выглядит он просто, а тесты на равенство редко кто пишет. Тем не менее часто встречается ситуация:Странное поведение системы ➡️ неделя дебага по всей бизнес-логике ➡️ ошибка в
equals ➡️ гнев и разочарование.Простой способ не ошибиться — не переопределять
equals вообще. Иногда это уместно:1️⃣ Для перечислений (
enum)2️⃣ Когда важны сами экземпляры, а не данные внутри них. Пример - классы
Thread, RecursiveTask.3️⃣ Сравнение объектов вообще не предполагается. Пример - класс
Pattern.4️⃣ В базовом классе уже есть
equals и нам подходит эта реализация.5️⃣ Класс имеет модификатор доступа
private или по умолчанию и мы уверены, что метод equals не будет вызван.В остальных случаях его лучше переопределить. Во многих проектах при этом используется аннотация
@EqualsAndHashCode библиотеки lombok, ограничения при её использовании мы тоже рассмотрим.Итак, при реализации
equals нужно учитывать следующие свойства:1️⃣ Рефлексивность — экземпляр должен быть равен сам себе.
2️⃣ Консистентность — если экземпляры равны, то без внешних воздействий они должны оставаться равны.
✅ Сравнивать только неизменяемые поля.
Чтобы обезопасить себя от проблем с
equals по возможности используйте для ключей в Map не классы целиком, а их неизменяемые поля, например, id.Если используете аннотацию
@EqualsAndHashCode явно указывайте поля, по которым идёт сравнение. Это поможет избежать ошибок при добавлении в класс изменяемого поля.✅
@EqualsAndHashCode(of={“x”,“y”})
3️⃣ Симметрия.Если в системе находятся объекты базового класса и его наследника нужно чётко понимать, как они будут между собой сравниваться.
Возьмём класс Точка, где равенство проверяется по координатам.
И его наследника — класс ЦветнаяТочка, где сравниваются координаты и цвет.
Точка обычная=new Точка(1,1);
Точка красная=new ЦветнаяТочка(1,1,RED);
Тогда обычная.equals(красная) будет true, а результат красная.equals(обычная) зависит от требований к системе — нужно ли игнорировать цвет в этом случае или нет.4️⃣ Транзитивность
.
Это свойство тоже легко нарушить, если мы имеем дело с иерархией:Точка красная=new ЦветнаяТочка(1,1,RED);
Точка обычная=new Точка(1,1);
Точка синяя=new ЦветнаяТочка(1,1,BLUE);
Если мы решили игнорировать цвет при сравнении разных типов, то допустима такая ситуация:красная.equals(обычная) - true обычная.equals(синяя) — trueкрасная.equals(синяя) - true 😥✅ Для иерархий классов писать юнит-тесты на симметричность и транзитивность.
✅ Не использовать
@EqualsAndHashCode для иерархий классов.🤔 Если в системе используются объекты базового и произвольного типов рассмотрите вариант замены наследования на композицию.
5️⃣ Сравнение существующего объекта с null должно возвращать false.
✅ Добавить отдельную проверку
if(o == null)
Итого:1️⃣ Не переопределяйте
equals, если сравнение не предполагается.2️⃣ Используйте в
equals только неизменяемые поля.3️⃣ Для иерархии классов пишите тесты на симметричность и транзитивность.
4️⃣ Для ключей в
Map желательно использовать неизменяемые поля, например, ID.5️⃣ Не использовать
@EqualsAndHashCode для иерархии классов.6️⃣ При использовании
@EqualsAndHashCode явно прописывайте поля.7️⃣ Кратко проверить два поля на null и равенство можно через
Objects.equals(this.a, а)
8️⃣ Проверяйте первыми поля, которые вероятнее всего отличаются.9️⃣ Не забывайте про
hashcode — используйте те же поля, что в методе equals.👍3❤1
Виш-лист разработчика: работа в стартапе и зарплата в долларах.
Пост рекламный, но о важных вещах.
В карьере разработчика сложны только первые шаги — войти в индустрию и набрать 3-4 года опыта. Спрос на программистов сильно превышает предложение, поэтому в IT большие возможности для самореализации.
Можно продвигаться в карьере, увеличивать зарплату или устраивать себе челленджи, например:
🔸️ Пройти путь от джуниора до архитектора за 5 лет.
🔸️ Разобраться в какой-нибудь теме, выступить на конференции и носить футболку с шуткой, которую поймут только 3 человека в мире.
🔸️ Пройти 8 этапов собеседований в Яндексе и отказаться от оффера.
Участвовать в молодых проектах — тоже интересная цель. Работа в стартапе совсем не похожа на уютный корпоративный мир. Это:
✅ супер увлечённые люди,
✅ ответственность и дисциплина,
✅ быстро видны результаты работы,
✅ топовые технологии.
Конечно, также велика вероятность:
▪️ ненормированного рабочего дня,
▪️ истеричных коллег,
▪️ закрытия проекта через 3 месяца.
Многие только после работы в стартапе поняли, что значить РАБОТАТЬ. Не закрывать задачи в джире и лениво отчитываться на стендапах, а решать проблемы здесь и сейчас, адаптироваться к изменениям, быстро внедрять новые технологии.
Работа с иностранными заказчиками — тоже специфический опыт. Удалённая работа, высокая зарплата, иностранный язык и иногда совсем другой менталитет.
Главная сложность — найти такие вакансии. На HeadHunter они если и появляются, то под названием «ИП Иванов А.И» или «ООО НьюСофт». Чаще всего их там нет🤷♂️
@profunctor_jobs — телеграм канал с супергорячими вакансиями и важной информацией — стек, зарплата, локация, график и контакт. Там ищут не только java разработчиков, но это реальный шанс найти интересные предложения. Среди их партнёров:
🤳Snap - компания владеет мессенджером Snapchat, умными очками Spectacles, сервисами Bitmoji и Zenly.
🚗Bolt – транспортная платформа, работающая в 30 странах.
💲Revolut – мультивалютные карты без процентов за конвертацию.
Рекомендуем подписаться и следить за вакансиями: @profunctor_jobs
Пост рекламный, но о важных вещах.
В карьере разработчика сложны только первые шаги — войти в индустрию и набрать 3-4 года опыта. Спрос на программистов сильно превышает предложение, поэтому в IT большие возможности для самореализации.
Можно продвигаться в карьере, увеличивать зарплату или устраивать себе челленджи, например:
🔸️ Пройти путь от джуниора до архитектора за 5 лет.
🔸️ Разобраться в какой-нибудь теме, выступить на конференции и носить футболку с шуткой, которую поймут только 3 человека в мире.
🔸️ Пройти 8 этапов собеседований в Яндексе и отказаться от оффера.
Участвовать в молодых проектах — тоже интересная цель. Работа в стартапе совсем не похожа на уютный корпоративный мир. Это:
✅ супер увлечённые люди,
✅ ответственность и дисциплина,
✅ быстро видны результаты работы,
✅ топовые технологии.
Конечно, также велика вероятность:
▪️ ненормированного рабочего дня,
▪️ истеричных коллег,
▪️ закрытия проекта через 3 месяца.
Многие только после работы в стартапе поняли, что значить РАБОТАТЬ. Не закрывать задачи в джире и лениво отчитываться на стендапах, а решать проблемы здесь и сейчас, адаптироваться к изменениям, быстро внедрять новые технологии.
Работа с иностранными заказчиками — тоже специфический опыт. Удалённая работа, высокая зарплата, иностранный язык и иногда совсем другой менталитет.
Главная сложность — найти такие вакансии. На HeadHunter они если и появляются, то под названием «ИП Иванов А.И» или «ООО НьюСофт». Чаще всего их там нет🤷♂️
@profunctor_jobs — телеграм канал с супергорячими вакансиями и важной информацией — стек, зарплата, локация, график и контакт. Там ищут не только java разработчиков, но это реальный шанс найти интересные предложения. Среди их партнёров:
🤳Snap - компания владеет мессенджером Snapchat, умными очками Spectacles, сервисами Bitmoji и Zenly.
🚗Bolt – транспортная платформа, работающая в 30 странах.
💲Revolut – мультивалютные карты без процентов за конвертацию.
Рекомендуем подписаться и следить за вакансиями: @profunctor_jobs
Сколько лет Вы пишете на java?
Anonymous Poll
39%
Меньше года
35%
1-2 года
12%
3-4 года
14%
Больше 4 лет
Какие темы Вам интересны?
Anonymous Poll
49%
Вопросы с собеседований
64%
Java core: обзор и тонкости использования
61%
Принципы разработки и лучшие практики
39%
Теория - JVM, компиляторы, детали реализации
24%
Soft skills - чему и где учиться, как эффективно работать и продвигаться в карьере и деньгах
23%
Саммари книжек и конференций по java
39%
Не совсем java - фреймворки, котлин, БД, архитектура
Привет! Чтобы писать понятные и полезные посты, мы хотим знать больше о Вас и о том, что Вам интересно. Ответьте, пожалуйста, на 2 вопроса выше☝️
Ещё 4 коротких вопроса в https://forms.gle/Vqdfpjfm59BmPQBa6
Ещё 4 коротких вопроса в https://forms.gle/Vqdfpjfm59BmPQBa6
Google Docs
Опрос аудитории Java: fill the gaps
Функциональный стиль в java: когда он нужен?
Большинство задач в энтерпрайзе делятся на две группы:
1️⃣ Одиночный запрос.
Покупка в интернет-магазине, вывод курса доллара — такие запросы затрагивают мало данных и много компонентов. Эти бизнес-процессы хорошо описываются объектами и отношениями между ними.
2️⃣ Обработка данных.
Статистика покупок, поиск рекомендаций, прогноз погоды - в центре внимания обработка данных, а не взаимодействие сущностей. Задача разработчика — чтобы обработка была быстрой. Нужно по максимуму использовать ресурсы процессора и доступную память, снизить время простоя и количество блокировок.
Вот здесь пригодятся некоторые подходы функционального программирования(ФП) :
1️⃣ Понятие чистой функции.
В многопоточном программировании главная проблема — работа с общими переменными. Чтобы безопасно их обновлять и всегда читать актуальные знвчения используются средства синхронизации. Большинство из них снижают общую пропускную способность.
Чистые функции - методы, которые характеризуются только входными и выходными данными. У них нет сайд-эффектов, Они не меняют внешних объектов и не бросают эксепшены. Для одинаковых входных данных результат всегда будет один и тот же.
✅ Нет общих переменных — нет синхронизации.
✅ Можно кэшировать некоторые результаты.
2️⃣ Неизменяемые данные
Исходные данные не должны меняться. Меняться могут только локальные переменные.
✅ Синхронизация не нужна — общие переменные не меняются и всегда актуальны.
Пример: добавление элемента в список.
Подход ООП: добавляем в исходный список - меняем внешний объект.
❌ Для запуска в многопоточной среде нужна синхронизация.
❌ Если метод прервался, то нужно выяснять, что он успел изменить, а что нет.
Подход ФП: создаём новый список на основе исходного и добавляем туда элемент.
✅ Если что-то пошло не так, просто ещё раз вызываем метод.
❌ Большой расход памяти.
3️⃣ Декларативный стиль написания кода.
Обход коллекции в цикле — это императивный подход. Для каждого элемента задаётся чёткая последовательность действий.
Stream API — декларативный. Благодаря большому количеству встроенных функций можно просто описать то, что нужно: отфильтровать, сгруппировать, просуммировать.
Лучшая читаемость - не единственное преимущество такого подхода. Чистые функции и неизменяемые данные сами по себе тоже не сделают программу эффективной. Но их использование в Stream API дают компилятору и JVM большие возможности для оптимизаций. Cамая мощная из них — опция parallel(), она автоматически распределяет данные по параллельным подзадачам.
Начиная с 8 версии java можно применять и другие идеи ФП — функции высших порядков, currying, pattern matching и т.д. Некоторые подходы были доступны и раньше, например, использовать хвостовую рекурсию вместо итерации. Но наибольшую практическую пользу можно извлечь из обработки больших коллекций с помощью Stream API.
Писать программы в функциональном стиле на чистой java можно, но на практике это пока не оптимально. Для обработки потоков данных больше подходят библиотеки для реактивного программирования и стрим-фреймворки.
Большинство задач в энтерпрайзе делятся на две группы:
1️⃣ Одиночный запрос.
Покупка в интернет-магазине, вывод курса доллара — такие запросы затрагивают мало данных и много компонентов. Эти бизнес-процессы хорошо описываются объектами и отношениями между ними.
2️⃣ Обработка данных.
Статистика покупок, поиск рекомендаций, прогноз погоды - в центре внимания обработка данных, а не взаимодействие сущностей. Задача разработчика — чтобы обработка была быстрой. Нужно по максимуму использовать ресурсы процессора и доступную память, снизить время простоя и количество блокировок.
Вот здесь пригодятся некоторые подходы функционального программирования(ФП) :
1️⃣ Понятие чистой функции.
В многопоточном программировании главная проблема — работа с общими переменными. Чтобы безопасно их обновлять и всегда читать актуальные знвчения используются средства синхронизации. Большинство из них снижают общую пропускную способность.
Чистые функции - методы, которые характеризуются только входными и выходными данными. У них нет сайд-эффектов, Они не меняют внешних объектов и не бросают эксепшены. Для одинаковых входных данных результат всегда будет один и тот же.
✅ Нет общих переменных — нет синхронизации.
✅ Можно кэшировать некоторые результаты.
2️⃣ Неизменяемые данные
Исходные данные не должны меняться. Меняться могут только локальные переменные.
✅ Синхронизация не нужна — общие переменные не меняются и всегда актуальны.
Пример: добавление элемента в список.
Подход ООП: добавляем в исходный список - меняем внешний объект.
public void add(List list, Integer value)
{ list.add(value); }
✅ Минимальный расход памяти.❌ Для запуска в многопоточной среде нужна синхронизация.
❌ Если метод прервался, то нужно выяснять, что он успел изменить, а что нет.
Подход ФП: создаём новый список на основе исходного и добавляем туда элемент.
public List add(List list, Integer value {
List newList=new List();
newList.addAll(list);
newList.add(value);
return list;
}
✅ Можно использовать в многопоточной среде.✅ Если что-то пошло не так, просто ещё раз вызываем метод.
❌ Большой расход памяти.
3️⃣ Декларативный стиль написания кода.
Обход коллекции в цикле — это императивный подход. Для каждого элемента задаётся чёткая последовательность действий.
Stream API — декларативный. Благодаря большому количеству встроенных функций можно просто описать то, что нужно: отфильтровать, сгруппировать, просуммировать.
Лучшая читаемость - не единственное преимущество такого подхода. Чистые функции и неизменяемые данные сами по себе тоже не сделают программу эффективной. Но их использование в Stream API дают компилятору и JVM большие возможности для оптимизаций. Cамая мощная из них — опция parallel(), она автоматически распределяет данные по параллельным подзадачам.
Начиная с 8 версии java можно применять и другие идеи ФП — функции высших порядков, currying, pattern matching и т.д. Некоторые подходы были доступны и раньше, например, использовать хвостовую рекурсию вместо итерации. Но наибольшую практическую пользу можно извлечь из обработки больших коллекций с помощью Stream API.
Писать программы в функциональном стиле на чистой java можно, но на практике это пока не оптимально. Для обработки потоков данных больше подходят библиотеки для реактивного программирования и стрим-фреймворки.
👍1
java.util.Set: сравнение реализаций.
Set - интерфейс для коллекций из уникальных элементов. В стандартной библиотеке java 6 реализаций:
HashSet, TreeSet, LinkedHashSet, CopyOnWriteArraySet, ConcurrentSkipListSet и EnumSet.
В этой статье рассмотрим и сравним первые три из них.
Детали реализации:
1️⃣ TreeSet базируется на TreeMap, а он - на основе красно-чёрного дерева. Элементы коллекции должны реализовать интерфейс Comparable, либо экземпляр Comparable нужно передать в конструкторе, потому что compareTo - основной метод для вставки и поиска элементов.
✅ Отсортированные значения.
✅ Быстрая ребалансировка.
✅ Можно искать по ближайшему ключу - доступны функции lower(e), floor(e), ceiling(e), higher(e).
2️⃣ HashSet внутри себя содержит HashMap. Он реализован так:
Есть массив корневых элементов - их ещё называют бакетами. Размер массива по умолчанию 16.
Если нужно добавить элемент в коллекцию, то сначала вычисляется его хэш. Далее ищется индекс корневого элемента - остаток от деления хэша на размер массива. Если по этому индексу ничего нет, корнем становится новый элемент. Если есть - добавляется в структуру под ним.
В java 7 этой структурой был список, с java 8 - бинарное дерево.
Эффективность HashSet зависит от равномерности распределения хэшей. В энтерпрайз приложениях хэш вычисляется по набору полей, и равномерность часто не достигается. Поэтому на практике HashSet не всегда является оптимальной структурой данных. Если известно примерное количество элементов, можно задать loadFactor и capacity в конструкторе.
✅ Возможность оптимизации.
✅ Быстрый поиск.
❌ Значения не отсортированы.
❌ Долгая перестройка при увеличении размера коллекции.
3️⃣ LinkedHashSet расширяет класс HashSet. Помимо основной структуры новые элемент также добавляются в двусвязный список.
✅ Значения отсортированы по порядку вставки.
✅ Возможность оптимизации.
Знание деталей реализации даёт простые правила выбора:
1️⃣ Частый поиск - выбираем TreeSet.
2️⃣ Порядок не важен - HashSet.
3️⃣ Порядок вставки важен - LinkedHashSet.
Мы проверили, насколько велика разница между реализациями:
1️⃣ Добавить в Set миллион чётных чисел.
2️⃣ Вставить миллион нечётных.
3️⃣ Найти миллион случайных.
Запустили несколько раз для HashSet, TreeSet, LinkedHashSet, а также для HashSet и LinkedHashSet с заранее заданной ёмкостью в 2 миллиона. Проверили на java 7 и 11.
В качестве типов данных использовали Integer(идеальное распределение хэшей) и класс с несколькими полями (неравномерное распределение).
Результаты на картинке внизу поста. Тест не претендует на точность, но теперь можно добавить ещё несколько рекомендаций:
✅ В новых версиях java все операции всех реализаций работают на 5-20% быстрее.
✅ Выбор реализации имеет значение.
✅ Знаете будущий размер коллекции - передавайте его в конструкторе.
✅ Для чисел лучше использовать HashSet.
✅ Для экземпляров классов всё неоднозначно, имеет смысл попробовать разные варианты.
Set - интерфейс для коллекций из уникальных элементов. В стандартной библиотеке java 6 реализаций:
HashSet, TreeSet, LinkedHashSet, CopyOnWriteArraySet, ConcurrentSkipListSet и EnumSet.
В этой статье рассмотрим и сравним первые три из них.
Детали реализации:
1️⃣ TreeSet базируется на TreeMap, а он - на основе красно-чёрного дерева. Элементы коллекции должны реализовать интерфейс Comparable, либо экземпляр Comparable нужно передать в конструкторе, потому что compareTo - основной метод для вставки и поиска элементов.
✅ Отсортированные значения.
✅ Быстрая ребалансировка.
✅ Можно искать по ближайшему ключу - доступны функции lower(e), floor(e), ceiling(e), higher(e).
2️⃣ HashSet внутри себя содержит HashMap. Он реализован так:
Есть массив корневых элементов - их ещё называют бакетами. Размер массива по умолчанию 16.
Если нужно добавить элемент в коллекцию, то сначала вычисляется его хэш. Далее ищется индекс корневого элемента - остаток от деления хэша на размер массива. Если по этому индексу ничего нет, корнем становится новый элемент. Если есть - добавляется в структуру под ним.
В java 7 этой структурой был список, с java 8 - бинарное дерево.
Эффективность HashSet зависит от равномерности распределения хэшей. В энтерпрайз приложениях хэш вычисляется по набору полей, и равномерность часто не достигается. Поэтому на практике HashSet не всегда является оптимальной структурой данных. Если известно примерное количество элементов, можно задать loadFactor и capacity в конструкторе.
✅ Возможность оптимизации.
✅ Быстрый поиск.
❌ Значения не отсортированы.
❌ Долгая перестройка при увеличении размера коллекции.
3️⃣ LinkedHashSet расширяет класс HashSet. Помимо основной структуры новые элемент также добавляются в двусвязный список.
✅ Значения отсортированы по порядку вставки.
✅ Возможность оптимизации.
Знание деталей реализации даёт простые правила выбора:
1️⃣ Частый поиск - выбираем TreeSet.
2️⃣ Порядок не важен - HashSet.
3️⃣ Порядок вставки важен - LinkedHashSet.
Мы проверили, насколько велика разница между реализациями:
1️⃣ Добавить в Set миллион чётных чисел.
2️⃣ Вставить миллион нечётных.
3️⃣ Найти миллион случайных.
Запустили несколько раз для HashSet, TreeSet, LinkedHashSet, а также для HashSet и LinkedHashSet с заранее заданной ёмкостью в 2 миллиона. Проверили на java 7 и 11.
В качестве типов данных использовали Integer(идеальное распределение хэшей) и класс с несколькими полями (неравномерное распределение).
Результаты на картинке внизу поста. Тест не претендует на точность, но теперь можно добавить ещё несколько рекомендаций:
✅ В новых версиях java все операции всех реализаций работают на 5-20% быстрее.
✅ Выбор реализации имеет значение.
✅ Знаете будущий размер коллекции - передавайте его в конструкторе.
✅ Для чисел лучше использовать HashSet.
✅ Для экземпляров классов всё неоднозначно, имеет смысл попробовать разные варианты.
Что выведет следующий код?
List list=new ArrayList();
list.add(1);
list.add(2);
Stream stream=list.stream();
list.remove(0);
println(stream.count());Stream API: обзор и основные методы.
Stream API - удобный инструмент обработки данных, который появился в java 8. Два принципа, на которых строится Stream API - composition & single responsibility. Действия собираются из простых независимых друг от друга блоков без сайд-эффектов. Этим достигается удобство чтения и поддержки:
✅ Стандартные названия.
✅ Нет локальных переменных.
✅ Линейная последовательность действий.
Стрим состоит из 3 частей:
1️⃣ Источник данных.
2️⃣ Преобразования.
3️⃣ Конечная операция.
Основные способы создать стрим:
🔸 Из готовой коллекции
🔸 Из набора элементов
🔸 Из массива
🔸 Задать первый элемент и правило вывода последующего, получится бесконечный стрим:
🔸 Генерировать независимые друг от друга элементы:
🔹
🔹
🔹
🔹
🔹
Вычисления выполняются не сразу, а только когда вызывается конечная операция. Конечная операция описывает желаемый результат. Результатом может быть:
◾️ Структура данных:
◾️ Результат поиска:
◾️ Сайд-эффект:
Стрим - не структура хранения данных. Он обходит источник данных (или сам его генерирует) и сохраняет промежуточные результаты. По этой причине нельзя копировать стримы и исполнять их несколько раз - пришлось бы контролировать этапы исполнения, консистентность данных и добавить синхронизацию для работы в многопоточной среде. При попытке переиспользования стрима выбрасывается
✅ Исходные данные не меняются.
✅ Стрим нельзя переиспользовать.
✅ Пока не вызвана конечная операция, стрим можно менять как угодно.
Пример из опроса выше вернёт 1.
Если Вас раздражает, что метод
Stream API - удобный инструмент обработки данных, который появился в java 8. Два принципа, на которых строится Stream API - composition & single responsibility. Действия собираются из простых независимых друг от друга блоков без сайд-эффектов. Этим достигается удобство чтения и поддержки:
✅ Стандартные названия.
✅ Нет локальных переменных.
✅ Линейная последовательность действий.
Стрим состоит из 3 частей:
1️⃣ Источник данных.
2️⃣ Преобразования.
3️⃣ Конечная операция.
Основные способы создать стрим:
🔸 Из готовой коллекции
🔸 Из набора элементов
🔸 Из массива
🔸 Задать первый элемент и правило вывода последующего, получится бесконечный стрим:
Stream.iterate(T, BinaryOperator)
🔸 Задать первый элемент, правило вывода последующего и условие остановки: Stream.iterate(T, Predicate, BinaryOperator) (java 9) 🔸 Генерировать независимые друг от друга элементы:
Stream.generate(Supplier)
🔸 Из диапазона с включением граничных значений: IntStream.range(..)
🔸 Диапазон без граничных значений: IntStream.rangeClosed(..)
🔸 Из строк: BufferedReader.lines()
🔸 Из символов строки: CharSequence.chars()
Названия большинства методов преобразований говорят сами за себя: filter, distinct, limit, sorted. Есть менее понятные:🔹
map: применить функцию к каждому элементу.🔹
flatmap: положить элементы из "списка списков" в единый стрим. Также с его помощью можно уменьшить размерность массива.🔹
takeWhile(Predicate): брать элементы из стрима, пока выполняется условие. Метод доступен c java 9.🔹
dropWhile(Predicate): пропускать элементы, пока условие не нарушится. Метод доступен c java 9.🔹
peek(Consumer): сделать что-то с каждым элементом стрима, не меняя исходный стрим. Часто используется при дебаге, например, вывести в консоль текущие элементы.Вычисления выполняются не сразу, а только когда вызывается конечная операция. Конечная операция описывает желаемый результат. Результатом может быть:
◾️ Структура данных:
toArray, collect.◾️ Результат поиска:
anyMatch, allMatch, nonMatch, findFirst, findAny.
◾️ Результат агрегации: min, max, count, reduce.◾️ Сайд-эффект:
forEach, forEachOrdered.Стрим - не структура хранения данных. Он обходит источник данных (или сам его генерирует) и сохраняет промежуточные результаты. По этой причине нельзя копировать стримы и исполнять их несколько раз - пришлось бы контролировать этапы исполнения, консистентность данных и добавить синхронизацию для работы в многопоточной среде. При попытке переиспользования стрима выбрасывается
IllegalStateException.✅ Исходные данные не меняются.
✅ Стрим нельзя переиспользовать.
✅ Пока не вызвана конечная операция, стрим можно менять как угодно.
Пример из опроса выше вернёт 1.
list.remove(0) удаляет элемент с индексом 0, поэтому на момент старта вычислений stream.count() в источнике данных остаётся всего один элемент.Если Вас раздражает, что метод
list.remove принимает не сам элемент, а индекс, то некоторые методы Stream API Вас тоже разочаруют. Например, такой код компилируется:Stream.of(-1, 0, 1).max(Math::max).get();Но в результате получается -1, потому что входной параметр метода
max - Comparator. Math.max по сигнатуре на него похож, поэтому получается неверный результат😒👍8
Stream API: забудьте про peek()
Один из недостатков Stream API по сравнению с циклами - неудобный дебаг. До сих пор метод
Один из недостатков Stream API по сравнению с циклами - неудобный дебаг. До сих пор метод
peek() был единственным способом посмотреть элементы внутри стрима:list.stream().filter(..)Недавно в Intellij IDEA вышел потрясающий апдейт, который выводит дебаг стримов на новый уровень:
.peek(e->println("filt:"+e))
.map(..)
.peek(e->println("map:"+e))
...
🔥6
10 правил безопасных java приложений.
Скорость, масштабируемость, удобство использования — такие требования обычно ставят заказчики. Устойчивость к атакам и защита данных редко проговаривается и подразумевается по умолчанию.
Для кода на java есть много рекомендаций, например, Secure Coding Guidelines for Java SE. На практике большинство ошибок можно избежать очень просто:
1️⃣ Понятный код и однозначная бизнес-логика - уязвимости легко пропустить в чём-то сложном и запутанном. Сужайте и скрывайте доступную функциональность на всех уровнях:
🔸 Модификаторы полей и классов: всё, что может быть private, делайте private. Если для задачи удобно поменять private на public, подумайте о других вариантах.
🔸 Reflection и проверка имён классов: используйте осторожно, т.к такой код не адаптируется к изменениям. Поменяется имя метода, добавится ещё один класс, а рефлекшн останется тем же.
🔸 Внешний API: чем проще, тем лучше.
2️⃣ Избегайте сериализации: чтобы хранить и передавать объекты внутри системы используйте облегчённые протоколы с поддержкой версионирования: gRPC, Avro, Thrift и т.д.
3️⃣ Шифруйте персональную информацию: пароли, номера карточек, кодовые слова. Не записывайте её в лог! В 2019 году перехват незащищённых данных всё ещё входит в топ-3 опасностей по версии OWASP.
4️⃣ Проверяйте входные данные от пользователей и сторонних систем. Даже если похожие проверки есть на фронтенде. Ограничивайте размер пользовательского ввода и результатов: не позволяйте загружать 5 ГБ картинку и запрашивать триллион записей.
5️⃣ Используйте PreparedStatement для запросов к БД. Для большинства NoSQL они тоже есть.
6️⃣ Скрывайте технические детали в сообщениях об ошибке: не показывайте стек трейс конечному пользователю и в консоли браузера.
7️⃣ Обновляйте библиотеки, не игнорируйте авто-апдейты.
8️⃣ Проверяйте open-source библиотеки, которые используются на проекте, например, с помощью OWASP Dependency-Check. Коммит вредоносного кода в open-source - реальная угроза.
9️⃣ Записывайте и анализируйте активность пользователей - как минимум неудачные попытки авторизации.
🔟 Тестируйте методы на переполнение, оно случается.
Скорость, масштабируемость, удобство использования — такие требования обычно ставят заказчики. Устойчивость к атакам и защита данных редко проговаривается и подразумевается по умолчанию.
Для кода на java есть много рекомендаций, например, Secure Coding Guidelines for Java SE. На практике большинство ошибок можно избежать очень просто:
1️⃣ Понятный код и однозначная бизнес-логика - уязвимости легко пропустить в чём-то сложном и запутанном. Сужайте и скрывайте доступную функциональность на всех уровнях:
🔸 Модификаторы полей и классов: всё, что может быть private, делайте private. Если для задачи удобно поменять private на public, подумайте о других вариантах.
🔸 Reflection и проверка имён классов: используйте осторожно, т.к такой код не адаптируется к изменениям. Поменяется имя метода, добавится ещё один класс, а рефлекшн останется тем же.
🔸 Внешний API: чем проще, тем лучше.
2️⃣ Избегайте сериализации: чтобы хранить и передавать объекты внутри системы используйте облегчённые протоколы с поддержкой версионирования: gRPC, Avro, Thrift и т.д.
3️⃣ Шифруйте персональную информацию: пароли, номера карточек, кодовые слова. Не записывайте её в лог! В 2019 году перехват незащищённых данных всё ещё входит в топ-3 опасностей по версии OWASP.
4️⃣ Проверяйте входные данные от пользователей и сторонних систем. Даже если похожие проверки есть на фронтенде. Ограничивайте размер пользовательского ввода и результатов: не позволяйте загружать 5 ГБ картинку и запрашивать триллион записей.
5️⃣ Используйте PreparedStatement для запросов к БД. Для большинства NoSQL они тоже есть.
6️⃣ Скрывайте технические детали в сообщениях об ошибке: не показывайте стек трейс конечному пользователю и в консоли браузера.
7️⃣ Обновляйте библиотеки, не игнорируйте авто-апдейты.
8️⃣ Проверяйте open-source библиотеки, которые используются на проекте, например, с помощью OWASP Dependency-Check. Коммит вредоносного кода в open-source - реальная угроза.
9️⃣ Записывайте и анализируйте активность пользователей - как минимум неудачные попытки авторизации.
🔟 Тестируйте методы на переполнение, оно случается.
❤5