Фрагменты, директивы и переменные
GraphQL — декларативный язык, но его мощь раскрывается по-настоящему, когда вы начинаете использовать фрагменты, директивы и переменные.
Эти инструменты позволяют:
избегать дублирования запросов,
включать или исключать поля динамически,
передавать аргументы безопасно,
строить сложные запросы, сохраняя читаемость.
1. Фрагменты
Фрагмент (fragment) — это именованный блок запроса, который описывает набор полей.
Он позволяет вынести общую часть и использовать её в нескольких местах.
Главная цель: избежать повторов полей.
Пример без фрагментов:
Версия с фрагментом
Как работает фрагмент
fragment UserBasic on User — объявление фрагмента.
User — тип, которому фрагмент принадлежит.
...UserBasic — вставка фрагмента.
Фрагменты можно вкладывать друг в друга и использовать с union и interface.
Кейсы, где фрагменты обязательны
Одинаковая структура данных в нескольких частях UI
Профили пользователей, карточки, списки.
Сложные запросы с глубокой вложенностью
Общие части выносятся в фрагменты.
Работа с interface и union
Позволяют описывать поля для нескольких типов.
Крупные запросы с десятками полей
Легче читать, тестировать и поддерживать.
2. Директивы
Директивы — это аннотации, которые модифицируют выполнение запроса.
Они влияют на структуру данных, которые вернёт сервер.
GraphQL имеет встроенные директивы и позволяет создавать свои.
2.1 Директива @include
Поле будет включено только если условие истинно.
2.2 Директива @skip
Обратная логика:
2.3 Директива @deprecated
Помечает поле устаревшим.
2.4 Кастомные директивы
Пример (схема):
Резолвер может модифицировать строку на лету (например, вернуть верхний регистр).
Это используется для логирования, авторизации, кеширования, маскирования данных.
3. Переменные в запросах
Переменные — это место, где GraphQL становится особенно полезным.
Они позволяют использовать один и тот же запрос с разными параметрами, не переписывая запрос.
3.1 Базовый пример
Запрос:
Переменные:
Сервер соединяет тело запроса и переменные и выполняет.
3.2 Переменные для input-объектов (в мутациях)
Мутация:
Переменные:
Это безопаснее, чем писать input прямо в теле мутации.
3.3 Комбинирование переменных с директивами
Комбинация:
один запрос,
два режима вывода данных,
ноль дублирования.
#Java #middle #GraphQL #Query
GraphQL — декларативный язык, но его мощь раскрывается по-настоящему, когда вы начинаете использовать фрагменты, директивы и переменные.
Эти инструменты позволяют:
избегать дублирования запросов,
включать или исключать поля динамически,
передавать аргументы безопасно,
строить сложные запросы, сохраняя читаемость.
1. Фрагменты
Фрагмент (fragment) — это именованный блок запроса, который описывает набор полей.
Он позволяет вынести общую часть и использовать её в нескольких местах.
Главная цель: избежать повторов полей.
Пример без фрагментов:
query {
user(id: 1) {
id
name
avatar
}
post(id: 10) {
author {
id
name
avatar
}
}
}
Повторяются три поля id, name, avatar.Версия с фрагментом
fragment UserBasic on User {
id
name
avatar
}
query {
user(id: 1) {
...UserBasic
}
post(id: 10) {
author {
...UserBasic
}
}
}
Теперь изменения структуры (например, добавление role) делаются в одном месте — фрагменте.Как работает фрагмент
fragment UserBasic on User — объявление фрагмента.
User — тип, которому фрагмент принадлежит.
...UserBasic — вставка фрагмента.
Фрагменты можно вкладывать друг в друга и использовать с union и interface.
Кейсы, где фрагменты обязательны
Одинаковая структура данных в нескольких частях UI
Профили пользователей, карточки, списки.
Сложные запросы с глубокой вложенностью
Общие части выносятся в фрагменты.
Работа с interface и union
Позволяют описывать поля для нескольких типов.
Крупные запросы с десятками полей
Легче читать, тестировать и поддерживать.
2. Директивы
Директивы — это аннотации, которые модифицируют выполнение запроса.
Они влияют на структуру данных, которые вернёт сервер.
GraphQL имеет встроенные директивы и позволяет создавать свои.
2.1 Директива @include
Поле будет включено только если условие истинно.
query GetUser($showAvatar: Boolean!) {
user(id: 1) {
name
avatar @include(if: $showAvatar)
}
}
Переменные:
{ "showAvatar": true }
Если showAvatar = false, поле avatar пропадёт из ответа целиком.2.2 Директива @skip
Обратная логика:
avatar @skip(if: $binaryMode)
Если binaryMode = true, поле будет пропущено.
2.3 Директива @deprecated
Помечает поле устаревшим.
type User {
username: String @deprecated(reason: "Use 'name' instead")
}
Эта директива влияет на документацию и IDE, но не на данные.2.4 Кастомные директивы
Пример (схема):
directive @upper on FIELD_DEFINITION
Резолвер может модифицировать строку на лету (например, вернуть верхний регистр).
Это используется для логирования, авторизации, кеширования, маскирования данных.
3. Переменные в запросах
Переменные — это место, где GraphQL становится особенно полезным.
Они позволяют использовать один и тот же запрос с разными параметрами, не переписывая запрос.
3.1 Базовый пример
Запрос:
query GetPost($id: ID!) {
post(id: $id) {
title
content
}
}Переменные:
{
"id": 100
}Сервер соединяет тело запроса и переменные и выполняет.
3.2 Переменные для input-объектов (в мутациях)
Мутация:
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
id
title
}
}Переменные:
{
"input": {
"title": "GraphQL",
"content": "Powerful queries"
}
}Это безопаснее, чем писать input прямо в теле мутации.
3.3 Комбинирование переменных с директивами
query User(
$id: ID!,
$withPosts: Boolean!
) {
user(id: $id) {
name
posts @include(if: $withPosts) {
title
}
}
}
Комбинация:
один запрос,
два режима вывода данных,
ноль дублирования.
#Java #middle #GraphQL #Query
👍2
4. Реальные производственные кейсы
Кейc 1: мобильная и web-версия используют разные наборы полей
Мобильный клиент получает минимум данных.
Web — максимум.
Кейc 2: переиспользование структуры пользователя
Все блоки страницы используют одну структуру.
CRM-интерфейсы любят такое.
Кейc 3: миграции схемы через директивы
Старая модель:
Frontend получает предупреждение в IDE — никаких тайных поломок.
Кейc 4: сложные фильтры в одном запросе
Переменные:
#Java #middle #GraphQL #Query
Кейc 1: мобильная и web-версия используют разные наборы полей
query User(
$id: ID!,
$isMobile: Boolean!
) {
user(id: $id) {
name
avatar @skip(if: $isMobile)
posts @include(if: $isMobile) {
title
}
}
}
Мобильный клиент получает минимум данных.
Web — максимум.
Кейc 2: переиспользование структуры пользователя
fragment UserCard on User {
id
name
avatar
}
query PageData {
recommendedUsers { ...UserCard }
recentVisitors { ...UserCard }
followers { ...UserCard }
}Все блоки страницы используют одну структуру.
CRM-интерфейсы любят такое.
Кейc 3: миграции схемы через директивы
Старая модель:
username @deprecated(reason: "Use 'login' instead")
Frontend получает предупреждение в IDE — никаких тайных поломок.
Кейc 4: сложные фильтры в одном запросе
query FilterPosts($filter: PostFilter!) {
posts(filter: $filter) {
id
title
}
}Переменные:
{
"filter": {
"authorId": 1,
"tags": ["graphql", "java"],
"minLikes": 10
}
}#Java #middle #GraphQL #Query
👍2
Что выведет код?
#Tasks
public class Task211125 {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "he" + "llo";
String s3 = "he";
String s4 = "llo";
String s5 = s3 + s4;
System.out.println(s1 == s2);
System.out.println(s1 == s5);
System.out.println(s1.equals(s5));
final String s6 = "he";
final String s7 = "llo";
String s8 = s6 + s7;
System.out.println(s1 == s8);
}
}#Tasks
👍1
Варианты ответа:
Anonymous Quiz
19%
true false true false
24%
true false true true
38%
true true true true
19%
false true true true
😱1
Вопрос с собеседований
Для чего используется CompletableFuture.anyOf()🤓
Ответ:
anyOf() запускает несколько асинхронных задач и завершается, как только выполнится любая.
Полезно, когда важен первый успешный результат.
Метод упрощает конкурентную работу, но требует аккуратного управления исключениями и понимания поведения оставшихся задач.
#собеседование
Для чего используется CompletableFuture.anyOf()
Ответ:
Полезно, когда важен первый успешный результат.
Метод упрощает конкурентную работу, но требует аккуратного управления исключениями и понимания поведения оставшихся задач.
#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
История IT-технологий сегодня — 22 ноября
ℹ️ Кто родился в этот день
Джеффри Дэвид Ульман (родился 22 ноября 1942 года) — американский теоретик информатики; автор классических учебников по компиляторам, теории вычислимости и базам данных, лауреат крупных наград в CS.
Википедия
Расмус Лердорф (дат. Rasmus Lerdorf; р. 22 ноября 1968) — датский программист, ныне живущий в Канаде, написавший в 1994 году набор скриптов на C, обрабатывающих шаблоны HTML-документов, позже воплотившийся в интерпретатор языка сценариев PHP, с помощью которого можно было решать различные задачи веб-приложений, включая не шаблонное создание сайтов для различных целей и направлений.
🌐 Знаковые события
1911 — лётчик Д. М. Сокольцов осуществил радиопередачу с самолёта на землю.
#Biography #Birth_Date #Events #22Ноября
Джеффри Дэвид Ульман (родился 22 ноября 1942 года) — американский теоретик информатики; автор классических учебников по компиляторам, теории вычислимости и базам данных, лауреат крупных наград в CS.
Википедия
Расмус Лердорф (дат. Rasmus Lerdorf; р. 22 ноября 1968) — датский программист, ныне живущий в Канаде, написавший в 1994 году набор скриптов на C, обрабатывающих шаблоны HTML-документов, позже воплотившийся в интерпретатор языка сценариев PHP, с помощью которого можно было решать различные задачи веб-приложений, включая не шаблонное создание сайтов для различных целей и направлений.
1911 — лётчик Д. М. Сокольцов осуществил радиопередачу с самолёта на землю.
#Biography #Birth_Date #Events #22Ноября
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
С 15.11 по 21.11
Предыдущий пост(с 08.11 по 14.11)
Воскресный мотивационный пост:
Не было мотивации
Запись встреч/видео:
JOOQ. Взаимодействуй с БД по-новому.
Обучающие статьи:
Java:
Коллекции в Java
Глава 2. List — списки в Java
Интерфейс List и его особенности
Глава 5. Map — отображения (словари)
Практика: В «Библиотеке» создать Map<String, Book> для быстрого поиска книги по названию.
GraphQL
Определение схемы в GraphQL (SDL)
Запросы и мутации в GraphQL
Фрагменты, директивы и переменные
Полезные статьи и видео:
Как написать приложение на JavaFX: гид для начинающих
MapStruct: как безобидный метод портит весь маппинг
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
Предыдущий пост(с 08.11 по 14.11)
Воскресный мотивационный пост:
Не было мотивации
Запись встреч/видео:
JOOQ. Взаимодействуй с БД по-новому.
Обучающие статьи:
Java:
Коллекции в Java
Глава 2. List — списки в Java
Интерфейс List и его особенности
Глава 5. Map — отображения (словари)
Практика: В «Библиотеке» создать Map<String, Book> для быстрого поиска книги по названию.
GraphQL
Определение схемы в GraphQL (SDL)
Запросы и мутации в GraphQL
Фрагменты, директивы и переменные
Полезные статьи и видео:
Как написать приложение на JavaFX: гид для начинающих
MapStruct: как безобидный метод портит весь маппинг
Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование
👍3
История IT-технологий сегодня — 23 ноября
ℹ️ Кто родился в этот день
Абхай Бхушан ( произношение на хинди: [əbʰəj bʰuːʂəɳː] ; родился 23 ноября 1944 г.) — индийский компьютерный учёный; автор оригинальной спецификации FTP и ряда ранних RFC для ARPANET/Интернета.
Магнус Даниэль Стенберг (23 ноября 1970) — шведский разработчик; создатель и ведущий разработчик проекта cURL, инструмента для работы с сетевыми протоколами, широко используемого в инфраструктуре.
🌐 Знаковые события
1924 — состоялась первая широковещательная передача Московского радио. Начало регулярного радиовещания в СССР.
Комментарий: Всего 100 лет назад... Только появилось радио, а теперь роботы, космос и интернет... Что будет еще через 100 лет?
#Biography #Birth_Date #Events #23Ноября
Абхай Бхушан ( произношение на хинди: [əbʰəj bʰuːʂəɳː] ; родился 23 ноября 1944 г.) — индийский компьютерный учёный; автор оригинальной спецификации FTP и ряда ранних RFC для ARPANET/Интернета.
Магнус Даниэль Стенберг (23 ноября 1970) — шведский разработчик; создатель и ведущий разработчик проекта cURL, инструмента для работы с сетевыми протоколами, широко используемого в инфраструктуре.
1924 — состоялась первая широковещательная передача Московского радио. Начало регулярного радиовещания в СССР.
Комментарий: Всего 100 лет назад... Только появилось радио, а теперь роботы, космос и интернет... Что будет еще через 100 лет?
#Biography #Birth_Date #Events #23Ноября
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Кем ты видишь себя в будущем в IT?
Сегодня в IT идёт каждый второй.
Кто-то по моде.
Кто-то за деньгами.
Но вот о чём почти никто не думает:
Кем ты хочешь стать через 5–10 лет?
И хочешь ли ты этого вообще?
Давайте об этом сегодня и поразмышляем.
Разработчик - это фундамент
Как бы банально ни звучало: разработчик - это основа IT.
Если убрать программистов, то рухнет всё: сервисы, приложения, бэкенды, интерфейсы - всё.
Но спустя годы работы некоторым становится тесно.
Кто-то устает просто решать таски.
Кому-то хочется больше влияния, кому-то — меньше рутины, кому-то — новых вызовов.
Куда можно расти дальше
1. Individual Contributor (IC): разработчик
Джун → мидл → сеньор → ведущий.
Ты пишешь код, решаешь сложные задачи, драйвишь качество.
Глубина и ширина.
Это путь для тех, кто любит и хочет продолжать создавать что-то своими руками.
2. Архитектор / системный инженер
От решения отдельных модулей - к проектированию больших систем.
Минимум строчек кода, больше схем, интеграций, рисков, компромиссов.
Тут ты уже не пишешь программу.
Ты формируешь её скелет, основу, генерируешь понимание как все это будет работать в связке, давая свою компетенцию и гарантии.
3. Тимлид (People Manager)
Тут код - уже не главное.
Главное - люди, процессы, сроки, коммуникация.
Мотивировать.
Помогать расти.
Разруливать конфликты.
Закрывать найм.
Ты превращаешься из разработчика в того, кто рулит и организует процесс разработки.
4. Техлид / Tech Lead
Эта позиция позволяет сохранить контроль над кодом, при этом руководя его написанием.
Ты остаёшься технически сильным, но получаешь влияние на команду.
Твоя зона: архитектура, ревью, стандарты, координация технического направления.
Без HR-функций, но с высокой ответственностью за качество.
5. Продуктовый путь (PM, Product Engineer)
Переход, от как запрограммировать - к а зачем это вообще нужно?
Пользовательские кейсы, метрики, ценность, гипотезы.
Ты становишься человеком, который превращает код в продукт, а не в набор функций.
А надо ли тебе это?
Когда приходит возможность выбирать - этот вопрос основной.
С одной стороны:
спокойная работа разработчиком, задачи, PR-ы, ревью, новые фреймворки и старое доброе легаси.
С другой:
рост, новые роли, новые риски, новые деньги.
Не каждому хочется руководить людьми.
Не каждый хочет отвечать за архитектуру.
Не всем нужен стресс, планирование, ответственность.
Кому-то достаточно закрывать таски и вечером залипать в сериалы 😂
И это тоже нормально.
Выбор есть у каждого.
Но решать придётся самому — трезво взвесив, чего ты реально хочешь.
Если статья зашла — поделись с другом и позови его на канал.
А мне — плюсик к карме 🤝😎
😎
#motivation
Сегодня в IT идёт каждый второй.
Кто-то по моде.
Кто-то за деньгами.
Но вот о чём почти никто не думает:
Кем ты хочешь стать через 5–10 лет?
И хочешь ли ты этого вообще?
Давайте об этом сегодня и поразмышляем.
Разработчик - это фундамент
Как бы банально ни звучало: разработчик - это основа IT.
Если убрать программистов, то рухнет всё: сервисы, приложения, бэкенды, интерфейсы - всё.
Но спустя годы работы некоторым становится тесно.
Кто-то устает просто решать таски.
Кому-то хочется больше влияния, кому-то — меньше рутины, кому-то — новых вызовов.
Куда можно расти дальше
1. Individual Contributor (IC): разработчик
Джун → мидл → сеньор → ведущий.
Ты пишешь код, решаешь сложные задачи, драйвишь качество.
Глубина и ширина.
Это путь для тех, кто любит и хочет продолжать создавать что-то своими руками.
2. Архитектор / системный инженер
От решения отдельных модулей - к проектированию больших систем.
Минимум строчек кода, больше схем, интеграций, рисков, компромиссов.
Тут ты уже не пишешь программу.
Ты формируешь её скелет, основу, генерируешь понимание как все это будет работать в связке, давая свою компетенцию и гарантии.
3. Тимлид (People Manager)
Тут код - уже не главное.
Главное - люди, процессы, сроки, коммуникация.
Мотивировать.
Помогать расти.
Разруливать конфликты.
Закрывать найм.
Ты превращаешься из разработчика в того, кто рулит и организует процесс разработки.
4. Техлид / Tech Lead
Эта позиция позволяет сохранить контроль над кодом, при этом руководя его написанием.
Ты остаёшься технически сильным, но получаешь влияние на команду.
Твоя зона: архитектура, ревью, стандарты, координация технического направления.
Без HR-функций, но с высокой ответственностью за качество.
5. Продуктовый путь (PM, Product Engineer)
Переход, от как запрограммировать - к а зачем это вообще нужно?
Пользовательские кейсы, метрики, ценность, гипотезы.
Ты становишься человеком, который превращает код в продукт, а не в набор функций.
А надо ли тебе это?
Когда приходит возможность выбирать - этот вопрос основной.
С одной стороны:
спокойная работа разработчиком, задачи, PR-ы, ревью, новые фреймворки и старое доброе легаси.
С другой:
рост, новые роли, новые риски, новые деньги.
Не каждому хочется руководить людьми.
Не каждый хочет отвечать за архитектуру.
Не всем нужен стресс, планирование, ответственность.
Кому-то достаточно закрывать таски и вечером залипать в сериалы 😂
И это тоже нормально.
Выбор есть у каждого.
Но решать придётся самому — трезво взвесив, чего ты реально хочешь.
Если статья зашла — поделись с другом и позови его на канал.
А мне — плюсик к карме 🤝😎
#motivation
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6
История IT-технологий сегодня — 24 ноября
ℹ️ Кто родился в этот день
Сельмер Брингсйорд (родился 24 ноября 1958 года) — американский исследователь в ИИ и когнитивных науках; известен работами по автоматическому рассуждению и философии машинного интеллекта.
🌐 Знаковые события
Не нашел((
#Biography #Birth_Date #Events #24Ноября
Сельмер Брингсйорд (родился 24 ноября 1958 года) — американский исследователь в ИИ и когнитивных науках; известен работами по автоматическому рассуждению и философии машинного интеллекта.
Не нашел((
#Biography #Birth_Date #Events #24Ноября
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Глава 2. List — списки в Java
Реализации: ArrayList и LinkedList. Сравнение производительности
ArrayList: динамический массив под капотом
Самая популярная и часто используемая реализация List. Её название раскрывает всю суть: ArrayList — это список, реализованный на основе массива.
Внутреннее устройство:
Массив как основа. Когда вы создаете ArrayList, внутри него создается обычный массив типа Object[] (или E[] после дженериков). Изначально этот массив имеет некоторый начальный размер (емкость, capacity), часто по умолчанию это 10 элементов.
Динамическое расширение.
Когда вы добавляете новый элемент с помощью add(), ArrayList проверяет, осталось ли место во внутреннем массиве.
Если место есть, элемент просто помещается в первую свободную ячейку elementData[size], и значение size увеличивается на 1. Это очень быстрая операция, comparable с работой с массивом.
Если массив полон, происходит следующее:
Создается новый массив большего размера. Стандартная логика увеличения — (старый_размер * 1.5) + 1.
Все элементы из старого массива копируются в новый.
Старый массив удаляется сборщиком мусора, а ссылка elementData начинает указывать на новый массив.
Только после этого новый элемент добавляется в конец.
Этот процесс пересоздания и копирования массива является относительно медленным, поэтому, если вы заранее знаете примерное количество элементов, лучше создать ArrayList с нужной начальной емкостью через конструктор new ArrayList<>(1000). Это позволит избежать или минимизировать количество операций расширения.
LinkedList: цепочка связанных элементов
LinkedList подходит к задаче иначе. Его название также прямо говорит о структуре: LinkedList — это связный список.
Внутреннее устройство:
Узлы (Node). LinkedList не использует массив. Вместо этого он построен на основе узлов.
Каждый узел — это самостоятельный объект, который хранит три вещи:
Сам элемент (например, строку или число).
Ссылку на следующий узел (next).
Ссылку на предыдущий узел (prev).
Двусвязность. LinkedList в Java является двусвязным списком. Это означает, что он хранит ссылки как на следующий, так и на предыдущий элемент. Благодаря этому можно легко перемещаться по списку как от начала к концу, так и от конца к началу.
Отсутствие массива. Элементы не хранятся в непрерывной области памяти. Они разбросаны по куче (Heap), а связаны между собой лишь этими ссылками-«ниточками». Голова списка — это поле first, а хвост — last.
#Java #для_новичков #beginner #List #ArrayList #LinkedList
Реализации: ArrayList и LinkedList. Сравнение производительности
ArrayList: динамический массив под капотом
Самая популярная и часто используемая реализация List. Её название раскрывает всю суть: ArrayList — это список, реализованный на основе массива.
Внутреннее устройство:
Массив как основа. Когда вы создаете ArrayList, внутри него создается обычный массив типа Object[] (или E[] после дженериков). Изначально этот массив имеет некоторый начальный размер (емкость, capacity), часто по умолчанию это 10 элементов.
// Примерно так выглядит внутри ArrayList
public class ArrayList<E> {
private Object[] elementData; // Внутренний массив
private int size; // Текущее количество реальных элементов
// ...
}
Динамическое расширение.
Когда вы добавляете новый элемент с помощью add(), ArrayList проверяет, осталось ли место во внутреннем массиве.
Если место есть, элемент просто помещается в первую свободную ячейку elementData[size], и значение size увеличивается на 1. Это очень быстрая операция, comparable с работой с массивом.
Если массив полон, происходит следующее:
Создается новый массив большего размера. Стандартная логика увеличения — (старый_размер * 1.5) + 1.
Все элементы из старого массива копируются в новый.
Старый массив удаляется сборщиком мусора, а ссылка elementData начинает указывать на новый массив.
Только после этого новый элемент добавляется в конец.
Этот процесс пересоздания и копирования массива является относительно медленным, поэтому, если вы заранее знаете примерное количество элементов, лучше создать ArrayList с нужной начальной емкостью через конструктор new ArrayList<>(1000). Это позволит избежать или минимизировать количество операций расширения.
LinkedList: цепочка связанных элементов
LinkedList подходит к задаче иначе. Его название также прямо говорит о структуре: LinkedList — это связный список.
Внутреннее устройство:
Узлы (Node). LinkedList не использует массив. Вместо этого он построен на основе узлов.
Каждый узел — это самостоятельный объект, который хранит три вещи:
Сам элемент (например, строку или число).
Ссылку на следующий узел (next).
Ссылку на предыдущий узел (prev).
// Примерная структура узла
private static class Node<E> {
E item; // Данные
Node<E> next; // Ссылка на следующий узел
Node<E> prev; // Ссылка на предыдущий узел
// ...
}
Двусвязность. LinkedList в Java является двусвязным списком. Это означает, что он хранит ссылки как на следующий, так и на предыдущий элемент. Благодаря этому можно легко перемещаться по списку как от начала к концу, так и от конца к началу.
Отсутствие массива. Элементы не хранятся в непрерывной области памяти. Они разбросаны по куче (Heap), а связаны между собой лишь этими ссылками-«ниточками». Голова списка — это поле first, а хвост — last.
#Java #для_новичков #beginner #List #ArrayList #LinkedList
👍1
Сравнение производительности
Время выполнения операций принято описывать в нотации "Big O", которая показывает, как время работы растет с увеличением объема данных (n).
1. Доступ к элементу по индексу (get(index))
ArrayList: O(1) — константное время.
Это его сильнейшая сторона. Поскольку внутри обычный массив, чтобы получить элемент по индексу 5, система просто делает одну операцию: берет начальный адрес массива и смещается на 5 ячеек в памяти. Это происходит мгновенно, независимо от размера списка.
LinkedList: O(n) — линейное время.
Это его главный недостаток для данной операции. У списка нет индексов в памяти. Чтобы найти элемент с индексом 5, ему приходится начинать с начала (или с конца, если индекс ближе к нему) и последовательно переходить по ссылкам next (или prev).
Для доступа к первому или последнему элементу (get(0) или get(last)) скорость будет высокой O(1), так как есть прямые ссылки first и last. Но для элемента в середине — очень низкой.
2. Вставка элемента (add(element)) и удаление с конца
ArrayList: В среднем O(1), но в худшем случае O(n).
Добавление в конец (add(element)) обычно очень быстрое (O(1)), так как это запись в свободную ячейку. Однако, если массив полон, требуется дорогостоящая операция копирования всего массива (O(n)).
LinkedList: O(1) — константное время.
Добавление в конец всегда выполняется за константное время. Для этого нужно просто создать новый узел, сделать его prev ссылку на старый последний узел, и обновить ссылку last. Это несколько операций, но их количество не зависит от размера списка.
3. Вставка/удаление в произвольной позиции (add(index, element), remove(index))
ArrayList: O(n) — линейное время.
Это его слабое место. Представьте, что вы вставляете элемент в начало списка (индекс 0). ArrayList вынужден сдвинуть все существующие элементы на одну позицию вправо, чтобы освободить место для нового.
Эта операция arraycopy требует времени, пропорционального количеству сдвигаемых элементов (n). Удаление из начала/середины имеет ту же проблему, так как требует сдвига всех последующих элементов влево.
LinkedList: В среднем O(n), но само изменение ссылок — O(1).
Время операции здесь определяется не самим добавлением/удалением, а поиском нужной позиции. Как мы помним, поиск по индексу в LinkedList занимает O(n). Однако, как только узел найден, вставка или удаление выполняются очень быстро: нужно всего лишь поменять несколько ссылок у соседних узлов. Не нужно перемещать половину списка!
Поэтому, если у вас уже есть ссылка на узел (например, вы находитесь в середине итерации), вставка и удаление рядом с этим узлом будут исключительно быстрыми (O(1)).
Когда использовать ArrayList (в 95% случаев):
Когда преобладают операции чтения и получения элементов по индексу.
Когда вы в основном добавляете элементы в конец.
Когда память несколько критична, и вы хотите минимизировать overhead.
Когда использовать LinkedList:
Когда преобладают операции вставки и удаления в начале или середине списка, и при этом у вас нет частой необходимости в быстром доступе по индексу.
Когда вы активно используете структуры типа "стек" (LIFO) или "очередь" (FIFO) (хогда для этого есть более специализированные классы, как ArrayDeque).
#Java #для_новичков #beginner #List #ArrayList #LinkedList
Время выполнения операций принято описывать в нотации "Big O", которая показывает, как время работы растет с увеличением объема данных (n).
1. Доступ к элементу по индексу (get(index))
ArrayList: O(1) — константное время.
Это его сильнейшая сторона. Поскольку внутри обычный массив, чтобы получить элемент по индексу 5, система просто делает одну операцию: берет начальный адрес массива и смещается на 5 ячеек в памяти. Это происходит мгновенно, независимо от размера списка.
// Внутренняя логика ArrayList.get(index)
public E get(int index) {
// ... проверка индекса ...
return (E) elementData[index]; // Прямое обращение по индексу массива
}
LinkedList: O(n) — линейное время.
Это его главный недостаток для данной операции. У списка нет индексов в памяти. Чтобы найти элемент с индексом 5, ему приходится начинать с начала (или с конца, если индекс ближе к нему) и последовательно переходить по ссылкам next (или prev).
// Примерная логика (упрощенно). Чтобы найти узел с индексом 5:
Node<E> x = first;
for (int i = 0; i < 5; i++) { // Нужно сделать 5 итераций
x = x.next;
}
return x.item;
Для доступа к первому или последнему элементу (get(0) или get(last)) скорость будет высокой O(1), так как есть прямые ссылки first и last. Но для элемента в середине — очень низкой.
2. Вставка элемента (add(element)) и удаление с конца
ArrayList: В среднем O(1), но в худшем случае O(n).
Добавление в конец (add(element)) обычно очень быстрое (O(1)), так как это запись в свободную ячейку. Однако, если массив полон, требуется дорогостоящая операция копирования всего массива (O(n)).
LinkedList: O(1) — константное время.
Добавление в конец всегда выполняется за константное время. Для этого нужно просто создать новый узел, сделать его prev ссылку на старый последний узел, и обновить ссылку last. Это несколько операций, но их количество не зависит от размера списка.
3. Вставка/удаление в произвольной позиции (add(index, element), remove(index))
ArrayList: O(n) — линейное время.
Это его слабое место. Представьте, что вы вставляете элемент в начало списка (индекс 0). ArrayList вынужден сдвинуть все существующие элементы на одну позицию вправо, чтобы освободить место для нового.
// При вставке в середину/начало в ArrayList
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = newElement;
size++;
Эта операция arraycopy требует времени, пропорционального количеству сдвигаемых элементов (n). Удаление из начала/середины имеет ту же проблему, так как требует сдвига всех последующих элементов влево.
LinkedList: В среднем O(n), но само изменение ссылок — O(1).
Время операции здесь определяется не самим добавлением/удалением, а поиском нужной позиции. Как мы помним, поиск по индексу в LinkedList занимает O(n). Однако, как только узел найден, вставка или удаление выполняются очень быстро: нужно всего лишь поменять несколько ссылок у соседних узлов. Не нужно перемещать половину списка!
// Вставка `newNode` между `prevNode` и `currentNode`
newNode.prev = prevNode;
newNode.next = currentNode;
prevNode.next = newNode;
currentNode.prev = newNode;
Поэтому, если у вас уже есть ссылка на узел (например, вы находитесь в середине итерации), вставка и удаление рядом с этим узлом будут исключительно быстрыми (O(1)).
Когда использовать ArrayList (в 95% случаев):
Когда преобладают операции чтения и получения элементов по индексу.
Когда вы в основном добавляете элементы в конец.
Когда память несколько критична, и вы хотите минимизировать overhead.
Когда использовать LinkedList:
Когда преобладают операции вставки и удаления в начале или середине списка, и при этом у вас нет частой необходимости в быстром доступе по индексу.
Когда вы активно используете структуры типа "стек" (LIFO) или "очередь" (FIFO) (хогда для этого есть более специализированные классы, как ArrayDeque).
#Java #для_новичков #beginner #List #ArrayList #LinkedList
👍1