├── .github └── workflows │ └── hugo.yaml ├── .gitignore ├── .gitmodules ├── .hugo_build.lock ├── README.md ├── TEMP ├── TEMP.md ├── Задача-1.md ├── Задача-10.md ├── Задача-12.md ├── Задача-15.md ├── Задача-16.md ├── Задача-18.md ├── Задача-19.md ├── Задача-2.md ├── Задача-26.md ├── Задача-27.md ├── Задача-28.md ├── Задача-29.md ├── Задача-3.md ├── Задача-30.md ├── Задача-31.md ├── Задача-32.md ├── Задача-33.md ├── Задача-34.md ├── Задача-38.md ├── Задача-39.md ├── Задача-40.md ├── Задача-6.md ├── Задача-8.md └── Задача-9.md ├── archetypes └── default.md ├── content ├── Tasks to think.md ├── Work experience.md ├── _index.md ├── livecoding.md ├── questions.md ├── Архитектура и разработка │ ├── Message broker.md │ ├── Microservices.md │ ├── Spring.md │ ├── Spring │ │ └── Spring_Bean_Life_cycle_Flow.png │ ├── WEB.md │ └── _index.md ├── Вопросы на подумать │ ├── Clean code, SOLID, patterns and refactoring.md │ ├── Debugging and performance.md │ ├── Git and build systems.md │ ├── Java core, Collections, OOP, Lambda, Immutable.md │ ├── Kafka, message brokers.md │ ├── Microservices, REST, System design, deploy.md │ ├── Multithreading.md │ ├── SQL, Database, index, ORM, Migrations.md │ ├── Spring Framework.md │ ├── Team processes.md │ └── _index.md ├── Задачи │ ├── _index.md │ ├── livecoding │ │ ├── task-livecoding-api-user-management.md │ │ ├── task-livecoding-check-palindrome.md │ │ ├── task-livecoding-decorator-pattern.md │ │ ├── task-livecoding-find-first-unique-element.md │ │ ├── task-livecoding-find-person-by-name.md │ │ ├── task-livecoding-find-two-sum-sorted.md │ │ ├── task-livecoding-find-two-sum-unsorted.md │ │ ├── task-livecoding-game-framework-refactoring.md │ │ ├── task-livecoding-http-get-contract.md │ │ ├── task-livecoding-http-post-contract.md │ │ ├── task-livecoding-java-binary-tree.md │ │ ├── task-livecoding-java-code-review-fixes.md │ │ ├── task-livecoding-java-contract-service.md │ │ ├── task-livecoding-java-crud-books.md │ │ ├── task-livecoding-java-fib-refactor.md │ │ ├── task-livecoding-java-fibonacci-sequence.md │ │ ├── task-livecoding-java-filter-users-in-place.md │ │ ├── task-livecoding-java-find-person-by-name.md │ │ ├── task-livecoding-java-first-non-repeating-element.md │ │ ├── task-livecoding-java-first-unique-char.md │ │ ├── task-livecoding-java-fix-message-building.md │ │ ├── task-livecoding-java-fix-spring-app-code-errors.md │ │ ├── task-livecoding-java-min-stack.md │ │ ├── task-livecoding-java-notification-service.md │ │ ├── task-livecoding-java-point-hashcode.md │ │ ├── task-livecoding-java-printformattedtext.md │ │ ├── task-livecoding-java-refactor-personservice.md │ │ ├── task-livecoding-java-rename-attribute.md │ │ ├── task-livecoding-java-repeated-string.md │ │ ├── task-livecoding-java-reverse-number.md │ │ ├── task-livecoding-java-reverse-string.md │ │ ├── task-livecoding-java-shoes-service.md │ │ ├── task-livecoding-java-singleton.md │ │ ├── task-livecoding-java-sort-array.md │ │ ├── task-livecoding-java-string-repetition.md │ │ ├── task-livecoding-partition-list.md │ │ ├── task-livecoding-singleton-pattern.md │ │ ├── task-livecoding-spring-auto-robot.md │ │ ├── task-livecoding-sql-authors-books.md │ │ ├── task-livecoding-sql-big-spenders.md │ │ ├── task-livecoding-sql-company-department-employee-count.md │ │ ├── task-livecoding-sql-customers-multiple-orders.md │ │ ├── task-livecoding-sql-departments-and-high-salary-employees.md │ │ ├── task-livecoding-sql-duplicate-names.md │ │ ├── task-livecoding-sql-empty-classes.md │ │ ├── task-livecoding-sql-export-news-with-comments.md │ │ ├── task-livecoding-sql-find-clients-by-inn-kpp.md │ │ ├── task-livecoding-sql-find-delegates-by-inn-kpp.md │ │ ├── task-livecoding-sql-fish-with-catch-less-than.md │ │ ├── task-livecoding-sql-folders-with-avi-or-empty.md │ │ ├── task-livecoding-sql-groups-without-services.md │ │ ├── task-livecoding-sql-match-strings-with-masks.md │ │ ├── task-livecoding-sql-max-price-by-category.md │ │ ├── task-livecoding-sql-min-max-salary-by-unit.md │ │ ├── task-livecoding-sql-min-max-salary.md │ │ ├── task-livecoding-sql-missing-users.md │ │ ├── task-livecoding-sql-most-frequent-value.md │ │ ├── task-livecoding-sql-multiple-cars-users.md │ │ ├── task-livecoding-sql-multiple-purchases-per-day.md │ │ ├── task-livecoding-sql-mutual-followers.md │ │ ├── task-livecoding-sql-n-last-news-with-comments.md │ │ ├── task-livecoding-sql-names-occurring-more-than-5.md │ │ ├── task-livecoding-sql-persons-payments.md │ │ ├── task-livecoding-sql-pink-cars-and-color-count.md │ │ ├── task-livecoding-sql-profiles-more-than-10-posts.md │ │ ├── task-livecoding-sql-select-top-5.md │ │ ├── task-livecoding-sql-services-january-2021-week.md │ │ ├── task-livecoding-sql-students-names-c.md │ │ ├── task-livecoding-sql-top-3-visitors-by-visit-count.md │ │ ├── task-livecoding-sql-top-sms-recipients.md │ │ ├── task-livecoding-sql-units-sold-after-time.md │ │ ├── task-livecoding-sql-user-car-relationship.md │ │ ├── task-livecoding-sql-users-multiple-cars.md │ │ ├── task-livecoding-sql-users-without-orders.md │ │ ├── task-livecoding-sql-users-without-trips.md │ │ ├── task-livecoding-stream-avoid-complex-lambdas.md │ │ ├── task-livecoding-stream-unique-active-item-names.md │ │ ├── task-livecoding-thread-sync-tick-tock.md │ │ ├── task-livecoding-token-limited-usage.md │ │ ├── task-livecoding-top-five-integers.md │ │ └── task-livecoding-url-shortener-service.md │ ├── main-livecoding-java.md │ ├── main-livecoding-sql.md │ └── main-livecoding-stream.md ├── Основы Java │ ├── Collections.md │ ├── Collections │ │ └── Collections.png │ ├── Exceptions.md │ ├── Exceptions │ │ └── Exception.png │ ├── Functional Interface.md │ ├── Generics.md │ ├── Java Core.md │ ├── Multithreading.md │ ├── Multithreading │ │ └── Life_Cycle_of_Thread.png │ ├── OOP.md │ ├── Stream API.md │ └── _index.md ├── Принципы и методы проектирования │ ├── Design Patterns.md │ ├── SOLID.md │ └── _index.md ├── Прочее │ ├── Other.md │ └── _index.md ├── Работа с базами данных │ ├── Database management system.md │ ├── Migrations.md │ ├── NoSQL.md │ ├── ORM.md │ ├── ORM │ │ └── Hibernate_Entity_Life_cycle.png │ ├── SQL.md │ ├── SQL │ │ ├── Analityc grouping.png │ │ └── B-Tree structure.png │ └── _index.md ├── Развертывание и инфраструктура │ ├── Deployment.md │ └── _index.md └── Тестирование │ ├── Testing.md │ └── _index.md ├── hugo.yaml ├── images ├── bot integration.png └── questions popularity.png └── resources └── _gen └── assets ├── book.scss_b807c86e8030af4cdc30edccea379f5f.content ├── book.scss_b807c86e8030af4cdc30edccea379f5f.json ├── book.scss_e129fe35b8d0a70789c8a08429469073.content └── book.scss_e129fe35b8d0a70789c8a08429469073.json /.github/workflows/hugo.yaml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a Hugo site to GitHub Pages 2 | name: Deploy Hugo site to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["master"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | # Default to bash 25 | defaults: 26 | run: 27 | shell: bash 28 | 29 | jobs: 30 | # Build job 31 | build: 32 | runs-on: ubuntu-latest 33 | env: 34 | HUGO_VERSION: 0.128.0 35 | steps: 36 | - name: Install Hugo CLI 37 | run: | 38 | wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \ 39 | && sudo dpkg -i ${{ runner.temp }}/hugo.deb 40 | - name: Install Dart Sass 41 | run: sudo snap install dart-sass 42 | - name: Checkout 43 | uses: actions/checkout@v4 44 | with: 45 | submodules: recursive 46 | - name: Setup Pages 47 | id: pages 48 | uses: actions/configure-pages@v5 49 | - name: Install Node.js dependencies 50 | run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true" 51 | - name: Build with Hugo 52 | env: 53 | HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache 54 | HUGO_ENVIRONMENT: production 55 | run: | 56 | hugo \ 57 | --minify \ 58 | --baseURL "${{ steps.pages.outputs.base_url }}/" 59 | - name: Upload artifact 60 | uses: actions/upload-pages-artifact@v3 61 | with: 62 | path: ./public 63 | 64 | # Deployment job 65 | deploy: 66 | environment: 67 | name: github-pages 68 | url: ${{ steps.deployment.outputs.page_url }} 69 | runs-on: ubuntu-latest 70 | needs: build 71 | steps: 72 | - name: Deploy to GitHub Pages 73 | id: deployment 74 | uses: actions/deploy-pages@v4 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | public/ 2 | content/Задачи/TEMP.md 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "themes/hugo-book"] 2 | path = themes/hugo-book 3 | url = https://github.com/alex-shpak/hugo-book 4 | -------------------------------------------------------------------------------- /.hugo_build.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/.hugo_build.lock -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-backend-interview-prep 2 | 3 | ## Локальный запуск 4 | 5 | Установить Hugo 6 | 7 | https://gohugo.io/installation/ 8 | 9 | Склонировать проект 10 | 11 | ```bash 12 | git clone git@github.com:zhukovsd/java-backend-interview-prep.git 13 | ``` 14 | 15 | Зайти в директорию проекта 16 | 17 | ```bash 18 | cd java-backend-interview-prep 19 | ``` 20 | Запустить локальный сервер с проектом 21 | 22 | ```bash 23 | hugo serve 24 | ``` 25 | 26 | Проект будет доступен на localhost:1313. Любое изменение в файлах проекта будет триггерить пересборку, т.е. все изменения будут отображаться на localhost 27 | 28 | --- 29 | 30 | Новый контент можно добавлять с помощью Hugo для правильной работы с метадатой в .md файлах 31 | 32 | ```bash 33 | hugo new content content/{file_name.md} 34 | ``` 35 | 36 | Сайт: 37 | > https://zhukovsd.github.io/java-backend-interview-prep/ 38 | 39 | -------------------------------------------------------------------------------- /TEMP/Задача-1.md: -------------------------------------------------------------------------------- 1 | #### 1. Задача. Каков порядок вызова конструкторов и блоков инициализации с учетом иерархии классов? 2 | 3 | ```java 4 | class C { 5 | public static void main(String[] args) { 6 | new B(); 7 | } 8 | } 9 | 10 | class A { 11 | public A() { 12 | print(); 13 | } 14 | 15 | public void print() { 16 | System.out.println(""From A""); 17 | } 18 | } 19 | 20 | class B extends A { 21 | private double pi = Math.PI; 22 | 23 | public B() { 24 | print(); 25 | } 26 | 27 | public void print() { 28 | System.out.println(pi); 29 | } 30 | } 31 | 32 | ``` 33 | 34 | {{< hint warning >}} 35 | **Спойлеры к решению**   36 | {{< /hint >}} 37 | 38 | {{< details "Подсказки" close >}} 39 | 1. Вызов конструктора класса `B`. Поскольку `B` является подклассом `A`, конструктор `B` автоматически вызывает конструктор суперкласса `A` с помощью неявного вызова `super()`. 40 | 41 | 2. Конструктор класса `A` вызывается первым, и он вызывает метод `print()`, который переопределен в классе `B`. Поскольку поле `pi` еще не инициализировано в этот момент, значение `pi` по умолчанию равно `0.0`, и именно это значение будет выведено. 42 | 43 | 3. После завершения конструктора класса `A`, инициализируется поле `pi` в классе `B`. 44 | 45 | 4. После завершения инициализации полей в классе `B`, выполняется оставшаяся часть конструктора `B`, который снова вызывает метод `print()`. На этот раз поле `pi` уже инициализировано, и выведется значение `Math.PI`. 46 | {{< /details >}} 47 | 48 | {{< details "Решение" close >}} 49 | ``` 50 | 0.0 51 | 52 | 3.141592653589793 53 | 54 | ``` 55 | {{< /details >}} 56 | -------------------------------------------------------------------------------- /TEMP/Задача-10.md: -------------------------------------------------------------------------------- 1 | #### 10. Логическая задача. В офис привезли три автомата с напитками. Первый выдаёт чай, второй кофе, а третий случайным образом чай или кофе. Стакан любого напитка стоит одну монету. На каждом автомате есть наклейка с названием продукта, который он выдаёт. Так получилось, что на заводе перепутали местами наклейки и на каждом автомате оказалась неправильная. Сколько нужно потратить монет, чтобы выяснить, где какой автомат? 2 | 3 | {{< hint warning >}} 4 | **Спойлеры к решению**   5 | {{< /hint >}} 6 | 7 | {{< details "Подсказки" close >}} 8 | - В каждом автомате наклейка неправильная. 9 | - Нужно выяснить, какой автомат выдает какой напиток, при этом можно проверять только один автомат за раз, и каждый напиток стоит одну монету. 10 | - Важно использовать минимальное количество монет для того, чтобы точно определить, какой автомат что выдаёт. 11 | {{< /details >}} 12 | 13 | {{< details "Решение" close >}} 14 | 15 | Чтобы минимизировать количество монет, используем стратегию, проверяя только один автомат. 16 | 17 | 1. Мы подходим к автомату, на котором наклейка говорит "случайно чай или кофе". Это означает, что этот автомат **не может** выдать случайный напиток. Он обязательно либо чай, либо кофе, в зависимости от того, какая наклейка на нем. 18 | 19 | 2. Подходим к этому автомату и получаем напиток: 20 | 21 | - Если автомат выдал чай, то это автомат с чаем. 22 | - Если автомат выдал кофе, то это автомат с кофе. 23 | 3. После того как мы определим, что это за напиток, мы можем сделать выводы о других автоматах, так как: 24 | 25 | - Если на автомате "случайно чай или кофе" был чай, значит автомат с наклейкой "чай" не может выдать чай, и он должен выдавать кофе. 26 | - Если на автомате "случайно чай или кофе" был кофе, значит автомат с наклейкой "кофе" не может выдать кофе, и он должен выдавать чай. 27 | 28 | **Ответ**: Чтобы выяснить, какой автомат какой напиток выдает, достаточно потратить **1 монету** на проверку автомата с наклейкой "случайно чай или кофе". 29 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-12.md: -------------------------------------------------------------------------------- 1 | #### 12. Задача. SQL. Как добавить связь между таблицами, чтобы один пользователь мог иметь несколько машин, а каждая машина принадлежала только одному пользователю? 2 | 3 | 4 | ```sql 5 | CREATE TABLE USER ( 6 | id INT, 7 | name VARCHAR(50) 8 | ); 9 | 10 | INSERT INTO USER (id, name) VALUES 11 | (1, 'Ivan'), 12 | (2, 'Oleg'), 13 | (3, 'Anna'), 14 | (4, 'Ivan'), 15 | (5, 'Ted'); 16 | 17 | CREATE TABLE CAR ( 18 | id INT, 19 | model VARCHAR(50) 20 | ); 21 | 22 | INSERT INTO CAR (id, model) VALUES 23 | (4422, 'Opel 1'), 24 | (4523, 'BMV 5'), 25 | (4612, 'VW'), 26 | (4853, 'BMV 6'); 27 | 28 | ``` 29 | 30 | 31 | 32 | 33 | {{< hint warning >}} 34 | **Спойлеры к решению**   35 | {{< /hint >}} 36 | 37 | {{< details "Подсказки" close >}} 38 | - Нам нужно связать пользователей (`USER`) и машины (`CAR`) так, чтобы **один пользователь мог иметь несколько машин**, но каждая машина принадлежала только **одному пользователю**. 39 | - Для этого в таблице **CAR** должен быть внешний ключ (`FOREIGN KEY`), который будет ссылаться на `id` пользователя. 40 | - Внешний ключ должен обеспечивать **целостность данных** — нельзя будет добавить машину без владельца. 41 | {{< /details >}} 42 | 43 | {{< details "Решение" close >}} 44 | 45 | ```sql 46 | CREATE TABLE USER ( 47 | id INT PRIMARY KEY, 48 | name VARCHAR(50) NOT NULL 49 | ); 50 | 51 | CREATE TABLE CAR ( 52 | id INT PRIMARY KEY, 53 | model VARCHAR(50) NOT NULL, 54 | user_id INT, -- Добавляем поле для связи с пользователем 55 | FOREIGN KEY (user_id) REFERENCES USER(id) ON DELETE CASCADE 56 | ); 57 | 58 | -- Наполняем таблицы данными 59 | INSERT INTO USER (id, name) VALUES 60 | (1, 'Ivan'), 61 | (2, 'Oleg'), 62 | (3, 'Anna'), 63 | (4, 'Ivan'), 64 | (5, 'Ted'); 65 | 66 | INSERT INTO CAR (id, model, user_id) VALUES 67 | (4422, 'Opel 1', 1), 68 | (4523, 'BMW 5', 2), 69 | (4612, 'VW', 3), 70 | (4853, 'BMW 6', 1); 71 | ``` 72 | 73 | 🔹 **Как это работает?** 74 | 75 | - Поле `user_id` в `CAR` указывает на `id` в `USER`, связывая машину с владельцем. 76 | - `ON DELETE CASCADE` — если пользователя удалят, все его машины тоже удалятся. 77 | 78 | 🔹 **Пример запроса:** 79 | Получить все машины конкретного пользователя: 80 | 81 | ```sql 82 | SELECT c.model 83 | FROM CAR c 84 | JOIN USER u ON c.user_id = u.id 85 | WHERE u.name = 'Ivan'; 86 | ``` 87 | 88 | **Выход:** 89 | 90 | ``` 91 | Opel 1 92 | BMW 6 93 | ``` 94 | 95 | Это означает, что **пользователь "Ivan" владеет Opel 1 и BMW 6**. 96 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-15.md: -------------------------------------------------------------------------------- 1 | #### 15. Задача. Дан код. Как работает код с использованием HashMap, когда по существующему ключу кладем другое значение? 2 | 3 | ```java 4 | public class Task6 { 5 | 6 | public static void main(String[] args) { 7 | List key = new ArrayList<>(List.of(""1"", ""2"", ""3"", ""4"")); 8 | Map map = new HashMap<>(); 9 | map.put(key, ""Hello World!""); 10 | key.add(randomString()); 11 | System.out.println(map.get(key)); 12 | } 13 | 14 | private static String randomString() { 15 | return ......; 16 | } 17 | } 18 | 19 | ``` 20 | 21 | {{< hint warning >}} 22 | **Спойлеры к решению**   23 | {{< /hint >}} 24 | 25 | {{< details "Подсказки" close >}} 26 | - `ArrayList` используется как ключ в `HashMap`. 27 | - Ключи в `HashMap` должны быть **иммутабельными** (нельзя менять). 28 | - `HashMap` использует `hashCode()` и `equals()` для поиска ключей. 29 | - Если изменить `key` после помещения в `HashMap`, его хэш изменится. 30 | - После изменения `key.add(randomString())` поиск `map.get(key)` сломается. 31 | {{< /details >}} 32 | 33 | {{< details "Решение" close >}} 34 | 35 | Этот код **не сможет** найти значение `"Hello World!"`, потому что: 36 | 37 | 1. `map.put(key, "Hello World!")` — `key` (список) становится ключом, его `hashCode` вычисляется. 38 | 2. `key.add(randomString())` — содержимое списка меняется, `hashCode` изменяется. 39 | 3. `map.get(key)` — `HashMap` ищет старый `hashCode`, но он уже изменился, **поэтому вернет `null`**. 40 | 41 | Правильный способ избежать этой ошибки: 42 | 43 | - **Использовать иммутабельный ключ**, например, `String` или `List.of(...)`. 44 | - **Клонировать изменяемые объекты** перед добавлением в `Map`. 45 | 46 | Исправленный вариант: 47 | 48 | ```java 49 | public class Task6 { 50 | public static void main(String[] args) { 51 | List key = new ArrayList<>(List.of("1", "2", "3", "4")); 52 | Map, String> map = new HashMap<>(); 53 | 54 | map.put(new ArrayList<>(key), "Hello World!"); // Клонируем key 55 | key.add(randomString()); 56 | 57 | System.out.println(map.get(key)); // Теперь key не изменяет существующий ключ 58 | } 59 | 60 | private static String randomString() { 61 | return UUID.randomUUID().toString(); 62 | } 63 | } 64 | ``` 65 | 66 | Теперь `map.get(key)` не сломается, но он все равно **не найдет** `"Hello World!"`, так как добавлен новый список. 67 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-16.md: -------------------------------------------------------------------------------- 1 | #### 16. Задача. Лайвкодинг. Как написать SQL-запрос, чтобы выбрать числа, которые дублируются более двух раз в числовом столбце таблицы? 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | - Для того, чтобы найти повторяющиеся значения в таблице, можно использовать **GROUP BY**. 10 | - С помощью **HAVING** можно задать условие, например, чтобы выбрать числа, которые встречаются более двух раз. 11 | - **COUNT()** позволяет подсчитать количество повторений каждого значения. 12 | {{< /details >}} 13 | 14 | {{< details "Решение" close >}} 15 | Запрос для нахождения чисел, которые дублируются более двух раз в числовом столбце таблицы: 16 | 17 | ```sql 18 | SELECT number 19 | FROM your_table 20 | GROUP BY number 21 | HAVING COUNT(number) > 2; 22 | ``` 23 | 24 | - **SELECT number**: выбираем столбец с числами. 25 | - **FROM your_table**: указываем таблицу, в которой ищем дубликаты. 26 | - **GROUP BY number**: группируем по числам, чтобы подсчитать количество повторений каждого числа. 27 | - **HAVING COUNT(number) > 2**: фильтруем те группы, где количество повторений больше двух. 28 | 29 | Замените `your_table` на имя вашей таблицы, а `number` на название столбца с числами. 30 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-18.md: -------------------------------------------------------------------------------- 1 | #### 18. Задачи. Как будет работать код с использованием Future, если для методов get() и set() используется synchronized, но по бизнес-логике не допускается перезапись переменной slot? Нужно ли применить паттерн double-check и как это исправить? 2 | 3 | ```java 4 | 5 | public class SettableFuture implements Future { 6 | private volatile V slot; 7 | 8 | public V get() throws InterruptedException, ExecutionException { 9 | synchronized(this) { 10 | while (slot == null) { 11 | wait(); 12 | } 13 | } 14 | return slot; 15 | } 16 | 17 | public void set(V value) { 18 | if (slot == null) { 19 | slot = value; 20 | synchronized(this) { 21 | notifyAll(); 22 | } 23 | } 24 | } 25 | } 26 | ``` 27 | 28 | {{< hint warning >}} 29 | **Спойлеры к решению**   30 | {{< /hint >}} 31 | 32 | {{< details "Подсказки" close >}} 33 | 34 | - В текущем коде используется синхронизация для методов `get()` и `set()`, но есть возможность перезаписать значение переменной `slot`, что не соответствует бизнес-логике. 35 | - Чтобы предотвратить нежелательную перезапись, нужно гарантировать, что метод `set()` не перезапишет уже установленное значение. 36 | - Паттерн **double-check** часто используется, чтобы избежать ненужной синхронизации в многопоточной среде, проверяя значение переменной дважды: один раз до блокировки и второй — внутри синхронизированного блока. 37 | - В данном случае нужно внести изменения, чтобы синхронизация не нарушала логику, и добавьте правильную проверку на перезапись. 38 | 39 | {{< /details >}} 40 | 41 | {{< details "Решение" close >}} 42 | 43 | Для того чтобы избежать перезаписи переменной `slot`, и при этом сохранить синхронизацию, можно внести следующие изменения: 44 | 45 | ```java 46 | public class SettableFuture implements Future { 47 | private volatile V slot; 48 | 49 | public V get() throws InterruptedException, ExecutionException { 50 | synchronized(this) { 51 | while (slot == null) { 52 | wait(); 53 | } 54 | } 55 | return slot; 56 | } 57 | 58 | public void set(V value) { 59 | if (slot == null) { // Проверка на null до синхронизации 60 | synchronized(this) { 61 | if (slot == null) { // Double-check внутри синхронизации 62 | slot = value; 63 | notifyAll(); 64 | } 65 | } 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | **Объяснение изменений:** 72 | 73 | 1. В методе `set()` добавлена проверка `if (slot == null)` перед синхронизацией. Это помогает избежать ненужной блокировки, если значение уже установлено. 74 | 2. После синхронизации добавлена дополнительная проверка `if (slot == null)` внутри синхронизированного блока. Это и есть паттерн **double-check**, который гарантирует, что значение будет установлено только один раз, даже если несколько потоков одновременно вызывают метод `set()`. 75 | 3. Таким образом, блокировка применяется только в случае, если необходимо установить значение, и это происходит только один раз. Это исправляет логику, не позволяя перезаписывать переменную `slot`. 76 | 77 | Теперь этот код работает корректно и не допускает перезаписи переменной, соблюдая нужную бизнес-логику, а паттерн double-check позволяет улучшить производительность, избегая лишней синхронизации. 78 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-2.md: -------------------------------------------------------------------------------- 1 | #### 2. Задача. Как написать обработчик сообщений в Spring, который будет отправлять сообщение в нужное место без использования if или switch? 2 | 3 | ```java 4 | public class Alert { 5 | 6 | private final String message; 7 | private final Date timestamp; 8 | } 9 | 10 | public class AlertService { 11 | 12 | public void handle(Alert alert) { 13 | 14 | } 15 | } 16 | 17 | ``` 18 | 19 | 20 | {{< hint warning >}} 21 | **Спойлеры к решению**   22 | {{< /hint >}} 23 | 24 | 25 | {{< details "Подсказки" close >}} 26 | Используйте паттерн "Стратегия" 27 | {{< /details >}} 28 | 29 | {{< details "Решение" close >}} 30 | Для написания обработчика сообщений в Spring, который будет отправлять сообщение в нужное место без использования `if` или `switch`, можно использовать шаблон проектирования "Стратегия". Это позволяет создавать динамически выбираемые обработчики на основе типа сообщения или других критериев. 31 | 32 | Вот пример, как это можно сделать: 33 | 34 | 1. Создайте интерфейс для стратегии: 35 | ```java 36 | public interface AlertHandler { 37 | void handle(Alert alert); 38 | } 39 | ``` 40 | 41 | 2. Реализуйте различные обработчики, которые будут реализовывать этот интерфейс: 42 | ```java 43 | @Service 44 | public class EmailAlertHandler implements AlertHandler { 45 | @Override 46 | public void handle(Alert alert) { 47 | // Логика отправки email 48 | System.out.println("Sending email alert: " + alert.getMessage()); 49 | } 50 | } 51 | 52 | @Service 53 | public class SmsAlertHandler implements AlertHandler { 54 | @Override 55 | public void handle(Alert alert) { 56 | // Логика отправки SMS 57 | System.out.println("Sending SMS alert: " + alert.getMessage()); 58 | } 59 | } 60 | ``` 61 | 62 | 1. Зарегистрируйте все обработчики в мапе, чтобы можно было динамически выбирать нужный обработчик: 63 | ```java 64 | @Service 65 | public class AlertService { 66 | 67 | private final Map handlerMap = new HashMap<>(); 68 | 69 | public AlertService(List handlers) { 70 | for (AlertHandler handler : handlers) { 71 | handlerMap.put(handler.getClass().getSimpleName(), handler); 72 | } 73 | } 74 | 75 | public void handle(String handlerName, Alert alert) { 76 | AlertHandler handler = handlerMap.get(handlerName); 77 | if (handler != null) { 78 | handler.handle(alert); 79 | } else { 80 | throw new IllegalArgumentException("No handler found for name: " + handlerName); 81 | } 82 | } 83 | } 84 | ``` 85 | 86 | Теперь вы можете динамически выбирать обработчик по имени, не используя `if` или `switch`. 87 | 88 | Пример использования: 89 | ```java 90 | public class Main { 91 | public static void main(String[] args) { 92 | Alert alert = new Alert("Test alert", new Date()); 93 | AlertService alertService = new AlertService(List.of(new EmailAlertHandler(), new SmsAlertHandler())); 94 | 95 | alertService.handle("EmailAlertHandler", alert); // Отправка email 96 | alertService.handle("SmsAlertHandler", alert); // Отправка SMS 97 | } 98 | } 99 | ``` 100 | 101 | Этот подход позволит вам легко добавлять новые типы обработчиков, не изменяя существующий код обработки сообщений. 😊 102 | 103 | {{< /details >}} 104 | 105 | -------------------------------------------------------------------------------- /TEMP/Задача-26.md: -------------------------------------------------------------------------------- 1 | #### 26. Задача. Какой вывод в консоль. Задача на String pool и метод intern() 2 | 3 | ```java 4 | 5 | public static void main(String[] args) { 6 | String s1 = ""abs""; 7 | String s2 = ""abs""; 8 | String s3 = new String(""abs""); 9 | 10 | System.out.println((s1 == s2)); 11 | System.out.println((s1 == s3)); 12 | System.out.println((s1.equals(s3))); 13 | } 14 | 15 | ``` 16 | 17 | 18 | {{< hint warning >}} 19 | **Спойлеры к решению**   20 | {{< /hint >}} 21 | 22 | {{< details "Подсказки" close >}} 23 | - В Java строки, созданные строковыми литералами (`""abc""`), автоматически попадают в **String Pool**. 24 | - Оператор `==` сравнивает ссылки на объекты, а `equals()` сравнивает содержимое. 25 | - `new String(""abc"")` создаёт **новый объект в heap**, даже если такая строка уже есть в пуле строк. 26 | {{< /details >}} 27 | 28 | {{< details "Решение" close >}} 29 | 30 | ```java 31 | public static void main(String[] args) { 32 | String s1 = "abs"; 33 | String s2 = "abs"; 34 | String s3 = new String("abs"); 35 | 36 | System.out.println((s1 == s2)); // true, обе ссылки указывают на один объект в String Pool 37 | System.out.println((s1 == s3)); // false, s3 — это новый объект в heap 38 | System.out.println((s1.equals(s3))); // true, так как содержимое строк одинаковое 39 | } 40 | ``` 41 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-27.md: -------------------------------------------------------------------------------- 1 | #### 27. Задача. Какой вывод в консоль. Работа с исключениями в Java и правильно обрабатывать несколько типов исключений, включая наследование исключений (например, FileNotFoundException — это подкласс IOException) 2 | 3 | 4 | ```java 5 | public static void main(String[] args) { 6 | try { 7 | throw new RuntimeException(""1""); 8 | } catch (Exception e) { 9 | throw new RuntimeException(""2""); 10 | } finally { 11 | throw new RuntimeException(""3""); 12 | } 13 | } 14 | 15 | ``` 16 | 17 | 18 | {{< hint warning >}} 19 | **Спойлеры к решению**   20 | {{< /hint >}} 21 | 22 | {{< details "Подсказки" close >}} 23 | 24 | - **Блок `finally` выполняется всегда**, даже если в `catch` есть `throw`. 25 | - Если в `catch` выбрасывается исключение, а затем в `finally` тоже выбрасывается новое исключение, **исключение из `finally` "затирает" предыдущее**. 26 | - Поэтому исключение `"3"` из `finally` станет окончательным исключением. 27 | 28 | {{< /details >}} 29 | 30 | {{< details "Решение" close >}} 31 | Код вызывает `RuntimeException("1")`, который ловится `catch (Exception e)`, затем выбрасывается `RuntimeException("2")`. 32 | Но `finally` тоже выбрасывает `RuntimeException("3")`, из-за чего `"2"` теряется. 33 | 34 | Вывод в консоль: 35 | 36 | ``` 37 | Exception in thread "main" java.lang.RuntimeException: 3 38 | at Main.main(Main.java:9) 39 | ``` 40 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-28.md: -------------------------------------------------------------------------------- 1 | #### 28. Задача. Два одинаковых метода, один с транзакцией другой без, что произойдет? Какие отличия между ними? 2 | 3 | ```java 4 | @Transactional(isolation = Isolation.SERIALIZABLE) 5 | public void example() { 6 | var account = accountRepository.findById(""1""); 7 | var firm = firmRepository.findById(""1""); 8 | 9 | firm.setName(""Bla bla bla""); 10 | 11 | account.setBalance(0); 12 | accountRepository.save(account); 13 | } 14 | 15 | public void example() { 16 | Account account = accountRepository.findById(""1""); 17 | Firm firm = firmRepository.findById(""1""); 18 | 19 | firm.setName(""Bla bla bla""); 20 | 21 | account.setBalance(0); 22 | accountRepository.save(account); 23 | } 24 | ``` 25 | 26 | 27 | {{< hint warning >}} 28 | **Спойлеры к решению**   29 | {{< /hint >}} 30 | 31 | {{< details "Подсказки" close >}} 32 | - Аннотация **`@Transactional`** используется для того, чтобы метод выполнялся как единственная атомарная операция, обеспечивая откат всех изменений в случае ошибки. 33 | - Уровень изоляции **`Isolation.SERIALIZABLE`** является самым строгим, и при его применении транзакции блокируют доступ к данным другим транзакциям до завершения текущей. 34 | - Без аннотации **`@Transactional`** изменения происходят независимо, что может привести к частичной записи данных при возникновении ошибки. 35 | {{< /details >}} 36 | 37 | {{< details "Решение" close >}} 38 | 39 | 1. **Метод с `@Transactional`** 🛠️ 40 | 41 | ```java 42 | @Transactional(isolation = Isolation.SERIALIZABLE) 43 | public void example() { 44 | var account = accountRepository.findById("1"); 45 | var firm = firmRepository.findById("1"); 46 | 47 | firm.setName("Bla bla bla"); // Это будет частью транзакции 48 | account.setBalance(0); 49 | accountRepository.save(account); // Это будет частью транзакции 50 | } 51 | ``` 52 | 53 | - Все изменения происходят в одной транзакции. 54 | - Если происходит ошибка в любом месте, то изменения откатываются, и данные остаются в том виде, в котором они были до начала транзакции. 55 | - **`Isolation.SERIALIZABLE`** блокирует другие транзакции от изменения этих данных, пока текущая транзакция не завершится. 56 | 57 | 2. **Метод без `@Transactional`** 🚫 58 | 59 | ```java 60 | public void example() { 61 | Account account = accountRepository.findById("1"); 62 | Firm firm = firmRepository.findById("1"); 63 | 64 | firm.setName("Bla bla bla"); // Это не будет частью транзакции 65 | account.setBalance(0); 66 | accountRepository.save(account); // Это не будет частью транзакции 67 | } 68 | ``` 69 | 70 | - Изменения в методах выполняются **независимо** друг от друга. 71 | - Если ошибка произойдет после изменения имени фирмы, но до сохранения счета, то изменение в фирме сохранится, но баланс **не будет изменен**. 72 | - Это может привести к **неконсистентности данных**, где одно изменение будет зафиксировано, а другое — нет. 73 | 74 | **Вывод** 75 | Метод с **`@Transactional`** гарантирует **атомарность** всех операций, предотвращая частичное сохранение данных. Без **`@Transactional`** возможно частичное сохранение данных, что может привести к ошибкам и неконсистентности в базе данных. 76 | {{< /details >}} 77 | -------------------------------------------------------------------------------- /TEMP/Задача-29.md: -------------------------------------------------------------------------------- 1 | #### 29. Задача. Картинка показывает фрагмент кода на Java, где создается HashSet, в который добавляются элементы, включая строки и объекты. Запрашивается, что будет выведено в консоль при вызове set.size() 2 | 3 | ```java 4 | class Scratch { 5 | public static void main(String[] args) { 6 | Set set = new HashSet<>(); 7 | set.add(""add-1""); 8 | set.add(""add-2""); 9 | set.add(""add-2""); 10 | set.add(new Object()); 11 | set.add(new Object()); 12 | 13 | System.out.println(set.size()); // что будет выведено? 14 | } 15 | } 16 | 17 | ``` 18 | 19 | 20 | 21 | {{< hint warning >}} 22 | **Спойлеры к решению**   23 | {{< /hint >}} 24 | 25 | {{< details "Подсказки" close >}} 26 | 1. **HashSet** хранит уникальные элементы, то есть добавление повторяющихся элементов будет проигнорировано. 27 | 2. Строки в Java являются объектами, и когда мы добавляем строковые литералы (например, `"add-2"`), они проверяются на идентичность через метод `equals()`. 28 | 3. Для объектов, добавленных через `new Object()`, HashSet будет различать их, так как они имеют разные ссылки в памяти. 29 | 4. Метод `size()` вернет количество уникальных элементов в наборе. 30 | 31 | {{< /details >}} 32 | 33 | {{< details "Решение" close >}} 34 | 35 | 1. Сначала добавляются строки `"add-1"`, `"add-2"` и снова `"add-2"`. Так как в HashSet одинаковые элементы (по методу `equals`) не могут быть добавлены, то `"add-2"` добавляется только один раз. 36 | 2. Далее добавляются два новых объекта. Каждый объект является уникальным, даже если их содержимое одинаково, потому что они имеют разные ссылки в памяти. 37 | 38 | Таким образом, в **set** окажется 4 элемента: `"add-1"`, `"add-2"`, и два различных объекта. 39 | 40 | Ответ: `4` 😎 41 | 42 | ```java 43 | class Scratch { 44 | public static void main(String[] args) { 45 | Set set = new HashSet<>(); 46 | set.add("add-1"); 47 | set.add("add-2"); 48 | set.add("add-2"); // Это не добавится 49 | set.add(new Object()); 50 | set.add(new Object()); 51 | 52 | System.out.println(set.size()); // Выведет 4 53 | } 54 | } 55 | ``` 56 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-3.md: -------------------------------------------------------------------------------- 1 | #### 3. Задача. Что выведет консоль? Задача на использование HashSet и обработку дубликатов 2 | 3 | ```java 4 | package org.example; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | public class Main { 10 | 11 | public static class Person { 12 | public String name; 13 | 14 | public Person(String name) { 15 | this.name = name; 16 | } 17 | } 18 | 19 | public static void main(String[] args) { 20 | Set persons = new HashSet<>(); 21 | Person p1 = new Person(""Петр""); 22 | Person p2 = new Person(""Алексей""); 23 | Person p3 = new Person(""Владимир""); 24 | Person p4 = new Person(""Петр""); 25 | persons = Set.of(p1, p2, p3, p4); 26 | System.out.println(persons.size()); // 27 | } 28 | } 29 | 30 | ``` 31 | 32 | {{< hint warning >}} 33 | **Спойлеры к решению**   34 | {{< /hint >}} 35 | 36 | {{< details "Подсказки" close >}} 37 | 38 | 1. **Как работает `HashSet`?** 39 | 40 | - `HashSet` хранит только **уникальные** элементы. 41 | - Определение уникальности основано на методах `equals()` и `hashCode()`. 42 | 2. **Как сравниваются объекты в `HashSet`?** 43 | 44 | - Если `equals()` и `hashCode()` **не переопределены**, сравнение идет **по ссылке в памяти** (разные объекты → разные элементы в `Set`). 45 | 3. **Что происходит в коде?** 46 | 47 | - Создаются 4 объекта `Person`, два из которых (`p1` и `p4`) имеют одинаковое имя `"Петр"`, но это **разные объекты в памяти**. 48 | - `Set.of(...)` создает **неизменяемый Set** (Java 9+). 49 | - Так как ссылки разные, `Set` добавляет все 4 объекта. 50 | {{< /details >}} 51 | 52 | {{< details "Решение" close >}} 53 | **Вывод консоли:** 54 | 55 | ``` 56 | 4 57 | ``` 58 | 59 | Все 4 объекта считаются разными, так как класс `Person` не переопределяет `equals()` и `hashCode()`. 60 | 61 | **Как исправить обработку дубликатов?** 62 | 63 | Добавить переопределение `equals()` и `hashCode()`: 64 | 65 | ```java 66 | public static class Person { 67 | public String name; 68 | 69 | public Person(String name) { 70 | this.name = name; 71 | } 72 | 73 | @Override 74 | public boolean equals(Object o) { 75 | if (this == o) return true; 76 | if (o == null || getClass() != o.getClass()) return false; 77 | Person person = (Person) o; 78 | return name.equals(person.name); 79 | } 80 | 81 | @Override 82 | public int hashCode() { 83 | return name.hashCode(); 84 | } 85 | } 86 | ``` 87 | 88 | **После исправления:** 89 | 90 | ``` 91 | 3 92 | ``` 93 | 94 | Теперь `HashSet` правильно определяет дубликаты по `name`. ✅ 95 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-30.md: -------------------------------------------------------------------------------- 1 | #### 30. Задача. Какой вывод в консоль. Работа с исключениями в Java и правильно обрабатывать несколько типов исключений, включая наследование исключений (например, FileNotFoundException — это подкласс IOException) 2 | 3 | ```java 4 | class Scratch { 5 | public static void main(String[] args) { 6 | try { 7 | e1(); 8 | e2(); 9 | } catch (Exception e) { 10 | } catch (IOException e) { 11 | } 12 | } 13 | 14 | public static void e1() throws IOException { 15 | throw new IOException(); 16 | } 17 | 18 | public static void e2() throws FileNotFoundException { 19 | throw new FileNotFoundException(); 20 | } 21 | } 22 | 23 | ``` 24 | 25 | {{< hint warning >}} 26 | **Спойлеры к решению**   27 | {{< /hint >}} 28 | 29 | {{< details "Подсказки" close >}} 30 | 31 | 1. В Java при обработке исключений важно правильно располагать блоки `catch`, потому что исключения могут наследоваться. 32 | 2. Если родительское исключение идет перед дочерним, то дочернее исключение будет недостижимо. 33 | 3. В этом случае `FileNotFoundException` — это подкласс `IOException`. Поэтому если в блоке `catch` сначала идет `Exception`, то исключение типа `IOException` будет поймано в первом блоке. А вот `FileNotFoundException`, являясь подклассом `IOException`, не попадет в блок `catch` для `IOException`, так как оно будет обработано первым блоком. 34 | {{< /details >}} 35 | 36 | {{< details "Решение" close >}} 37 | 38 | В данном коде будет ошибка компиляции, так как блок `catch (IOException e)` идет после блока `catch (Exception e)`, и компилятор не может понять, какой из этих блоков должен обработать `IOException` (который является родителем для `FileNotFoundException`). 39 | 40 | Ошибка будет следующей: `exception IOException is already caught by Exception`. 41 | 42 | Чтобы исправить, нужно поменять порядок блоков `catch`, чтобы сначала шел блок для `IOException`, а потом для более специфического `FileNotFoundException`. 43 | 44 | **Правильное исправление:** 45 | 46 | ```java 47 | class Scratch { 48 | public static void main(String[] args) { 49 | try { 50 | e1(); 51 | e2(); 52 | } catch (IOException e) { 53 | // обрабатываем IOException, включая FileNotFoundException 54 | } catch (FileNotFoundException e) { 55 | // это исключение не будет вызвано 56 | } 57 | } 58 | 59 | public static void e1() throws IOException { 60 | throw new IOException(); 61 | } 62 | 63 | public static void e2() throws FileNotFoundException { 64 | throw new FileNotFoundException(); 65 | } 66 | } 67 | ``` 68 | {{< /details >}} 69 | -------------------------------------------------------------------------------- /TEMP/Задача-31.md: -------------------------------------------------------------------------------- 1 | #### 31. Задача. Создать HashMap с пользовательским классом Id в качестве ключа. В классе Id переопределены методы equals (возвращает всегда true) и hashCode (возвращает хэш-код строки или 0). В цикле от 0 до 99 добавлять в HashMap 100 объектов Id с уникальными значениями строк и проверять размер карты. Найти ошибку в коде 2 | 3 | ```java 4 | class Scratch { 5 | 6 | public static void main(String[] args) { 7 | Map idMap = new HashMap<>(); 8 | 9 | for (int i = 0; i < 100; i++) { 10 | idMap.put(new Id(String.valueOf(i)), String.valueOf(i * i)); 11 | } 12 | 13 | System.out.println(idMap.size()); 14 | } 15 | 16 | public static class Id { 17 | 18 | private String idVal; 19 | 20 | public Id(String idVal) { 21 | this.idVal = idVal; 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | return true; 27 | } 28 | 29 | @Override 30 | public int hashCode() { 31 | return idVal != null ? idVal.hashCode() : 0; 32 | } 33 | } 34 | } 35 | 36 | ``` 37 | 38 | 39 | 40 | {{< hint warning >}} 41 | **Спойлеры к решению**   42 | {{< /hint >}} 43 | 44 | {{< details "Подсказки" close >}} 45 | 🔹 **equals() всегда возвращает true**, что значит, что все объекты считаются равными друг другу. 46 | 🔹 **hashCode() возвращает корректное значение**, но из-за equals() это не спасает. 47 | 🔹 HashMap считает, что все ключи равны, а значит, **значение постоянно перезаписывается**. 48 | 🔹 В итоге размер карты **будет равен 1, а не 100**. 49 | {{< /details >}} 50 | 51 | {{< details "Решение" close >}} 52 | Ошибка в том, что метод `equals()` всегда возвращает `true`. В HashMap при добавлении нового элемента сначала вычисляется хэш-код, затем проверяется равенство через `equals()`. Если `equals()` всегда возвращает `true`, то HashMap считает все объекты одинаковыми и просто заменяет предыдущее значение. 53 | 54 | **Исправленный вариант**: 55 | Необходимо исправить метод `equals()`, чтобы он корректно сравнивал объекты: 56 | 57 | ```java 58 | @Override 59 | public boolean equals(Object o) { 60 | if (this == o) return true; 61 | if (o == null || getClass() != o.getClass()) return false; 62 | Id id = (Id) o; 63 | return Objects.equals(idVal, id.idVal); 64 | } 65 | ``` 66 | 67 | Теперь `HashMap` будет корректно хранить 100 уникальных объектов 🎯 68 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-32.md: -------------------------------------------------------------------------------- 1 | #### 32. Задача. Создать HashMap с пользовательским классом Id в качестве ключа. Класс Id: Содержит приватное поле idVal типа String. Переопределяет метод equals, чтобы сравнивать объекты на основе значения idVal. Переопределяет метод hashCode, возвращающий хэш-код idVal или 0. В цикле от 0 до 99 добавлять в HashMap 100 объектов Id с уникальными значениями строк и проверять размер карты. Найти ошибку в коде 2 | 3 | ```java 4 | class Scratch { 5 | public static void main(String[] args) { 6 | Map idMap = new HashMap<>(); 7 | 8 | for (int i = 0; i < 100; i++) { 9 | idMap.put(new Id(String.valueOf(i)), String.valueOf(i * i)); 10 | } 11 | 12 | System.out.println(idMap.size()); 13 | } 14 | 15 | public static class Id { 16 | private String idVal; 17 | 18 | public Id(String idVal) { 19 | this.idVal = idVal; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) return true; 25 | if (!(o instanceof Id)) return false; 26 | Id id = (Id) o; 27 | if (idVal != null ? !idVal.equals(id.idVal) : id.idVal != null) return false; 28 | return true; 29 | } 30 | 31 | @Override 32 | public int hashCode() { 33 | return idVal != null ? idVal.hashCode() : 0; 34 | } 35 | } 36 | } 37 | 38 | ``` 39 | 40 | 41 | {{< hint warning >}} 42 | **Спойлеры к решению**   43 | {{< /hint >}} 44 | 45 | {{< details "Подсказки" close >}} 46 | 🔹 `equals()` выглядит **почти правильным**, но сравнение строк через `!=` и `!equals()` можно упростить. 47 | 🔹 **hashCode() возвращает корректное значение**, но проблема в `equals()`. 48 | 🔹 Код должен работать правильно, но можно сделать его чище и избежать потенциальных ошибок. 49 | {{< /details >}} 50 | 51 | {{< details "Решение" close >}} 52 | Ошибка в том, что проверка `if (idVal != null ? !idVal.equals(id.idVal) : id.idVal != null)` **избыточна и сложна**. Ее можно заменить стандартным вызовом `Objects.equals()`, который учитывает `null` автоматически. 53 | 54 | **Исправленный вариант**: 55 | 56 | ```java 57 | @Override 58 | public boolean equals(Object o) { 59 | if (this == o) return true; 60 | if (o == null || getClass() != o.getClass()) return false; 61 | Id id = (Id) o; 62 | return Objects.equals(idVal, id.idVal); 63 | } 64 | ``` 65 | 66 | 💡 Теперь код стал **чище и безопаснее**. `Objects.equals()` уже проверяет `null`, поэтому нам не нужно вручную писать сложные условия. 67 | При запуске `HashMap` теперь **корректно сохранит 100 уникальных объектов** 🎯 68 | {{< /details >}} 69 | -------------------------------------------------------------------------------- /TEMP/Задача-33.md: -------------------------------------------------------------------------------- 1 | #### 33. Задача. Поиск архитектурной ошибки 2 | 3 | 4 | ```java 5 | class Shapes { 6 | 7 | public static void main(String[] args) { 8 | GeometricShape geometricShape = new GeometricShape(ShapeType.CIRCLE); 9 | geometricShape.printMe(); 10 | } 11 | 12 | public abstract static class Shape { 13 | protected final ShapeType type; 14 | protected Shape(ShapeType type) { this.type = type; } 15 | 16 | abstract ShapeType getType(); 17 | abstract void printMe(); 18 | } 19 | 20 | public static class GeometricShape extends Shape { 21 | public GeometricShape(ShapeType type) { super(type); } 22 | @Override 23 | ShapeType getType() { return super.type; } 24 | @Override 25 | void printMe() { 26 | System.out.println(type.name()); 27 | } 28 | } 29 | 30 | public enum ShapeType { 31 | CIRCLE, 32 | SQUARE, 33 | TRIANGLE 34 | } 35 | } 36 | 37 | ``` 38 | 39 | 40 | {{< hint warning >}} 41 | **Спойлеры к решению**   42 | {{< /hint >}} 43 | 44 | {{< details "Подсказки" close >}} 45 | 🔹 `Shape` является **абстрактным классом**, но его метод `getType()` избыточен, так как поле `type` уже доступно в `Shape`. 46 | 🔹 `GeometricShape` **не добавляет новой логики**, а просто передает `type`. Это нарушает принцип **Liskov Substitution Principle (LSP)**. 47 | 🔹 Код можно улучшить, избавившись от **избыточного наследования**. 48 | {{< /details >}} 49 | 50 | {{< details "Решение" close >}} 51 | Ошибка заключается в том, что **наследование здесь не нужно**. Абстрактный класс `Shape` и конкретный `GeometricShape` не добавляют ничего полезного. Вместо этого можно просто использовать `ShapeType` как поле в объекте. 52 | 53 | **Исправленный вариант**: 54 | 55 | ```java 56 | class Shapes { 57 | public static void main(String[] args) { 58 | GeometricShape shape = new GeometricShape(ShapeType.CIRCLE); 59 | shape.printMe(); 60 | } 61 | 62 | public static class GeometricShape { 63 | private final ShapeType type; 64 | public GeometricShape(ShapeType type) { this.type = type; } 65 | public void printMe() { System.out.println(type.name()); } 66 | } 67 | 68 | public enum ShapeType { 69 | CIRCLE, SQUARE, TRIANGLE 70 | } 71 | } 72 | ``` 73 | 74 | 💡 Теперь **наследование убрано**, и код стал **проще и чище**! 🎯 75 | {{< /details >}} 76 | -------------------------------------------------------------------------------- /TEMP/Задача-38.md: -------------------------------------------------------------------------------- 1 | #### 38. Задача. Какой результат выполнения програмы будет? 2 | 3 | 4 | ```java 5 | public class Scratch { 6 | 7 | public static void main(String[] args) { 8 | String str = ""abc""; 9 | methodReplace(str); 10 | System.out.println(str); 11 | } 12 | 13 | public static void methodReplace(String str) { 14 | str = ""def""; 15 | } 16 | } 17 | ``` 18 | 19 | {{< hint warning >}} 20 | **Спойлеры к решению**   21 | {{< /hint >}} 22 | 23 | {{< details "Подсказки" close >}} 24 | 🔹 В Java строки (`String`) **иммутабельны** – их нельзя изменить после создания. 25 | 🔹 Метод `methodReplace` принимает строку **по значению** (копию ссылки), а не сам объект. 26 | 🔹 Изменение внутри метода **не влияет** на оригинальную переменную. 27 | {{< /details >}} 28 | 29 | {{< details "Решение" close >}} 30 | 31 | 🔹 Код содержит ошибку синтаксиса (`""abc""` → `"abc"`). Исправленный вариант: 32 | 33 | ```java 34 | public class Scratch { 35 | public static void main(String[] args) { 36 | String str = "abc"; 37 | methodReplace(str); 38 | System.out.println(str); // ??? 39 | } 40 | 41 | public static void methodReplace(String str) { 42 | str = "def"; // Меняем ЛОКАЛЬНУЮ переменную, не исходную 43 | } 44 | } 45 | ``` 46 | 47 | 🔹 **Вывод в консоль:** 48 | 49 | ``` 50 | abc 51 | ``` 52 | 53 | 🚀 **Вывод:** Строка **не изменится**, так как `methodReplace` работает с локальной копией ссылки, а не с оригинальным объектом. 54 | {{< /details >}} 55 | 56 | 57 | -------------------------------------------------------------------------------- /TEMP/Задача-39.md: -------------------------------------------------------------------------------- 1 | #### 39. Задача. Какой результат выполнения програмы будет? Вопрос на знания string pool и equals 2 | 3 | ```java 4 | public class Scratch { 5 | 6 | public static void main(String[] args) { 7 | String a = ""123a""; 8 | String b = ""123a""; 9 | String c = new String(""123a""); 10 | String d = ""123A""; 11 | 12 | System.out.println(a.equals(b)); 13 | System.out.println(a == b); 14 | System.out.println(a == c); 15 | System.out.println(a.equals(d)); 16 | System.out.println(a.equals(c)); 17 | } 18 | } 19 | 20 | ``` 21 | 22 | 23 | {{< hint warning >}} 24 | **Спойлеры к решению**   25 | {{< /hint >}} 26 | 27 | {{< details "Подсказки" close >}} 28 | 🔹 В **String Pool** хранятся только литералы строк (строки, объявленные через `""`). 29 | 🔹 Оператор `==` сравнивает **ссылки на объекты**, а не их содержимое. 30 | 🔹 Метод `equals` сравнивает **содержимое строк**. 31 | 🔹 Когда создается строка через `new`, она не будет храниться в пуле, а будет выделен новый объект. 32 | {{< /details >}} 33 | 34 | {{< details "Решение" close >}} 35 | 36 | 1. **a.equals(b)**: Сравнение строк по содержимому. `a` и `b` — это литералы, хранящиеся в **String Pool**, поэтому результат будет `true`. 37 | 2. **a == b**: Оператор `==` сравнивает ссылки. Так как обе строки — литералы, они указывают на один и тот же объект в String Pool, поэтому результат будет `true`. 38 | 3. **a == c**: Строка `c` создается с помощью `new`, и эта строка не будет ссылаться на объект из String Pool. Поэтому результат будет `false`. 39 | 4. **a.equals(d)**: Сравнение строк по содержимому. Строки `a` и `d` имеют разные символы (`'a'` и `'A'`), поэтому результат будет `false`. 40 | 5. **a.equals(c)**: Сравнение строк по содержимому. Строки `a` и `c` идентичны, хотя `c` была создана через `new`, содержимое одинаково. Поэтому результат будет `true`. 41 | 42 | 🔹 **Вывод программы:** 43 | 44 | ``` 45 | true 46 | true 47 | false 48 | false 49 | true 50 | ``` 51 | 52 | 📌 **Объяснение:** 53 | 54 | - Литералы в Java хранятся в **String Pool** и всегда сравниваются по ссылке и содержимому, если они одинаковы. 55 | - Строки, созданные через `new String(...)`, не используют пул строк, поэтому их ссылки будут отличаться. 56 | {{< /details >}} 57 | -------------------------------------------------------------------------------- /TEMP/Задача-40.md: -------------------------------------------------------------------------------- 1 | #### 40. Задача. Какой результат выполнения програмы будет? Вопрос на знания блока try/catch/finally 2 | 3 | ```java 4 | public class Scratch { 5 | public static void main(String[] args) { 6 | System.out.println(div(10, 5)); 7 | System.out.println(div(10, 0)); 8 | 9 | private static int div(int a, int b) { 10 | try { 11 | return a / b; 12 | } catch (Exception ex) { 13 | return Integer .MAX_VALUE; 14 | } finally { 15 | return Integer .MIN_VALUE; 16 | 17 | ``` 18 | 19 | 20 | {{< hint warning >}} 21 | **Спойлеры к решению**   22 | {{< /hint >}} 23 | 24 | {{< details "Подсказки" close >}} 25 | 🔹 В **try** блоке выполняется основной код. 26 | 🔹 В **catch** блоке можно перехватить исключения и обработать их. 27 | 🔹 Блок **finally** всегда выполняется, независимо от того, было ли исключение. Если в нем есть `return`, он может переопределить возвращаемое значение из других блоков. 28 | 29 | {{< /details >}} 30 | 31 | {{< details "Решение" close >}} 32 | 33 | 1. **div(10, 5)**: 34 | 35 | - В **try** блоке делаем 10 / 5, результат `2`. 36 | - Блок **finally** всегда выполняется, и `Integer.MIN_VALUE` (минимальное значение для `int`, -2147483648) перезаписывает результат. 37 | **Вывод:** `-2147483648`. 38 | 2. **div(10, 0)**: 39 | 40 | - В **try** блоке возникает исключение деления на ноль (`ArithmeticException`), поэтому выполняется **catch** блок. В нем возвращается `Integer.MAX_VALUE` (максимальное значение для `int`, 2147483647). 41 | - Блок **finally** выполняется после **catch**, и возвращает `Integer.MIN_VALUE`, перезаписывая результат из **catch**. 42 | **Вывод:** `-2147483648`. 43 | 44 | 🔹 **Вывод программы:** 45 | 46 | ``` 47 | -2147483648 48 | -2147483648 49 | ``` 50 | 51 | 📌 **Объяснение:** 52 | 53 | - Даже если в блоках `try` или `catch` возвращается значение, блок `finally` выполняется в любом случае и его результат перезаписывает возвращаемое значение. 54 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-6.md: -------------------------------------------------------------------------------- 1 | #### 6. Задача. Что выведется на консоль при использовании метода filter() в Stream API? 2 | 3 | ```java 4 | class Main { 5 | public static void main(String[] args) { 6 | Stream mainStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 7 | List listOdd = mainStream.filter(item -> item % 2 == 0).collect(toList()); 8 | System.out.println(listOdd); 9 | 10 | List listEven = mainStream.filter(item -> item % 2 != 0).collect(toList()); 11 | System.out.println(listEven); 12 | } 13 | } 14 | 15 | public static boolean isPalindrome(String text) { 16 | return true; 17 | } 18 | ``` 19 | 20 | {{< hint warning >}} 21 | **Спойлеры к решению**   22 | {{< /hint >}} 23 | 24 | {{< details "Подсказки" close >}} 25 | - Метод `filter()` фильтрует элементы потока, применяя заданное условие. 26 | - Поток (Stream) можно пройти только один раз. После первого использования `mainStream` становится **закрытым**. 27 | - Для вывода фильтрованных элементов нужно перезапустить поток или использовать коллекцию, чтобы повторно его использовать. 28 | {{< /details >}} 29 | 30 | {{< details "Решение" close >}} 31 | 32 | 1. В первом `filter()`, где выбираются четные элементы (`item % 2 == 0`), список будет пустым, так как поток уже закрыт после первого использования. 33 | 2. Во втором `filter()`, где выбираются нечетные элементы (`item % 2 != 0`), произойдет то же самое, поскольку поток уже закрыт. 34 | 35 | Пример правильного кода с повторным созданием потока: 36 | 37 | ```java 38 | import java.util.List; 39 | import java.util.stream.Stream; 40 | import static java.util.stream.Collectors.toList; 41 | 42 | class Main { 43 | public static void main(String[] args) { 44 | Stream mainStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 45 | 46 | // Применяем filter() для четных чисел 47 | List listOdd = mainStream.filter(item -> item % 2 == 0).collect(toList()); 48 | System.out.println(listOdd); // [2, 4, 6, 8, 10] 49 | 50 | // Создаем новый поток для нечетных чисел 51 | mainStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 52 | List listEven = mainStream.filter(item -> item % 2 != 0).collect(toList()); 53 | System.out.println(listEven); // [1, 3, 5, 7, 9] 54 | } 55 | } 56 | ``` 57 | 58 | Вывод: 59 | 60 | ``` 61 | [2, 4, 6, 8, 10] 62 | [1, 3, 5, 7, 9] 63 | ``` 64 | 65 | Без перезапуска потока код бы не работал. ❌ 66 | {{< /details >}} -------------------------------------------------------------------------------- /TEMP/Задача-9.md: -------------------------------------------------------------------------------- 1 | #### 9. Логическая задача. Четыре человека находятся на одной стороне моста ночью, и им нужно перейти на другую сторону. Из доступных средств у них есть только один фонарик, без которого переход через мост невозможен. Мост достаточно узкий, поэтому по нему могут идти не более двух человек одновременно. У каждого человека разная скорость перехода: первому нужно 1 минуту, второму - 3 минуты, третьему - 6 минут, четвертому - 8 минут. Когда двое идут вместе, они двигаются со скоростью самого медленного. 2 | 3 | {{< hint warning >}} 4 | **Спойлеры к решению**   5 | {{< /hint >}} 6 | 7 | {{< details "Подсказки" close >}} 8 | - На мосту могут быть одновременно только два человека, и скорость перехода определяется самым медленным из них. 9 | - У нас есть только один фонарик, который необходимо нести, чтобы переходить мост. 10 | - Нужно минимизировать общее время, которое потребуется для того, чтобы все четыре человека оказались на другой стороне моста. 11 | {{< /details >}} 12 | 13 | {{< details "Решение" close >}} 14 | Для решения этой задачи нужно продумать стратегию, при которой наиболее медленные люди будут переправляться с минимальными затратами времени. Рассмотрим следующий подход: 15 | 16 | 1. **Первый шаг:** Пусть сначала на другой берег переходят два самых быстрых человека (1 минута и 3 минуты). 17 | 18 | - Время: 3 минуты. 19 | - На противоположной стороне: 1 и 3. 20 | - На начальной стороне: 6 и 8. 21 | 2. **Второй шаг:** Самый быстрый человек (1 минута) возвращается с фонариком на исходную сторону. 22 | 23 | - Время: 1 минута. 24 | - На противоположной стороне: 3. 25 | - На начальной стороне: 1, 6, 8. 26 | 3. **Третий шаг:** Теперь переходят два самых медленных человека (6 и 8 минут). 27 | 28 | - Время: 8 минут. 29 | - На противоположной стороне: 3, 6, 8. 30 | - На начальной стороне: 1. 31 | 4. **Четвертый шаг:** Самый быстрый человек (3 минуты) возвращается с фонариком на начальную сторону. 32 | 33 | - Время: 3 минуты. 34 | - На противоположной стороне: 6, 8. 35 | - На начальной стороне: 1, 3. 36 | 5. **Пятый шаг:** Остальные два человека (1 и 3 минуты) снова переходят на другую сторону. 37 | 38 | - Время: 3 минуты. 39 | - На противоположной стороне: 1, 3, 6, 8. 40 | 41 | Итого: 3 + 1 + 8 + 3 + 3 = **18 минут**. 42 | {{< /details >}} -------------------------------------------------------------------------------- /archetypes/default.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: {{ replace .File.ContentBaseName "-" " " | title }} 3 | date: {{ .Date }} 4 | weight: 1 5 | --- 6 | -------------------------------------------------------------------------------- /content/Tasks to think.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Вопросы на подумать' 3 | weight = 3 4 | bookFlatSection = true 5 | bookToC = false 6 | +++ 7 | 8 | # Вопросы на подумать 9 | 10 | --- 11 | 12 | ### 1. [Основы Java, коллекции, OOP, лямбды, иммутабельность](Вопросы-на-подумать/Java-core,-Collections,-OOP,-Lambda,-Immutable.md) 13 | 14 | --- 15 | ### 2. [Многопоточность](Вопросы-на-подумать/Multithreading.md) 16 | 17 | --- 18 | ### 3. [SQL, базы данных, индексы, ORM и миграции](Вопросы-на-подумать/SQL,-Database,-index,-ORM,-Migrations.md) 19 | 20 | --- 21 | ### 4. [Spring Framework](Вопросы-на-подумать/Spring-Framework.md) 22 | 23 | --- 24 | ### 5. [Kafka и брокеры сообщений](Вопросы-на-подумать/Kafka,-message-brokers.md) 25 | 26 | --- 27 | ### 6. [Микросервисы, REST, дизайн систем, деплой](Вопросы-на-подумать/Microservices,-REST,-System-design,-deploy.md) 28 | 29 | --- 30 | ### 7. [Отладка и производительность](Вопросы-на-подумать/Debugging-and-performance.md) 31 | 32 | --- 33 | ### 8. [Чистый код, SOLID, паттерны и рефакторинг](Вопросы-на-подумать/Clean-code,-SOLID,-patterns-and-refactoring.md) 34 | 35 | --- 36 | ### 9. [Git и системы сборки](Вопросы-на-подумать/Git-and-build-systems.md) 37 | 38 | --- 39 | ### 10. [Командные процессы](Вопросы-на-подумать/Team-processes.md) 40 | 41 | --- -------------------------------------------------------------------------------- /content/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Главная страница 3 | date: 2024-10-03T23:15:53+07:00 4 | --- 5 | 6 | # IT Ментор | Java методичка 7 | 8 | Добро пожаловать! Это методичка от сообщества [IT Ментор | Сергей Жуков](https://t.me/zhukovsd_it_chat) для подготовки к собеседованиям на позицию Java разработчика. 9 | 10 | Другие наши ресурсы: 11 | - [Java роадмап](https://zhukovsd.github.io/java-backend-learning-course/) 12 | - [Telegram канал](https://t.me/zhukovsd_it_mentor) 13 | - [YouTube канал](https://youtube.com/@zhukovsd_it_mentor) 14 | - [Чат](https://t.me/+8YGzqvJOndg1Yjky) для проведения мок собеседований 15 | 16 | ## Какую проблему решает 17 | 18 | Главная задача, которую решает методичка - помощь в подготовке к **актуальным** вопросам. Всего не запомнишь, количество вопросов по теории может достигать сотен. В нашей методичке более 300 вопросов, но всего 85 из них встречаются хотя бы на каждом десятом собеседовании. 19 | 20 | Рядом с каждым [техническим вопросом](https://zhukovsd.github.io/java-backend-interview-prep/questions/) в методичке отображается его популярность на собеседованиях. 21 | 22 | Это значение автоматически подсчитывается на основе таймкодов к коллекции собеседований сообщества. На 15 декабря 2024 в коллекции 110 собеседований. 23 | 24 | ## Что внутри 25 | 26 | - [Технические вопросы](https://zhukovsd.github.io/java-backend-interview-prep/questions/) с % популярности каждого. 27 | - Ответы на технические вопросы - каждый вопрос по ссылке выше это ссылка на ответ. 28 | - [Коллекция](https://zhukovsd.github.io/java-backend-interview-prep/work-experience/) вопросов про опыт работы, встречаются на 90% собеседований. 29 | - [Вопросы на подумать](https://zhukovsd.github.io/java-backend-interview-prep/tasks-to-think/), встречаются на 62% собеседований. 30 | - [Задачи на лайвкодинг](https://zhukovsd.github.io/java-backend-interview-prep/livecoding/) по Java, SQL, Stream API, рефакторингу с реальных собеседований. Встречаются на 51% собеседований. 31 | 32 | ## Как пользоваться 33 | 34 | Рекомендуемый план подготовки к собеседованиям: 35 | 1. Идти по темам по порядку, описанному в [списке технических вопросов](https://zhukovsd.github.io/java-backend-interview-prep/questions/). 36 | 2. В рамках каждой темы разбирать вопросы, которые встречаются хотя бы на 5-10% собеседований или чаще. Советую не читать ответы, чтобы их запоммнить, а разбираться самостоятельно, ведя свой собственный конспект. 37 | 3. Прийти в [чат](https://t.me/+8YGzqvJOndg1Yjky) сообщества для подготовки по теории и попросить мок собеседование по одной или нескольким темам. 38 | 4. Пройдя все темы, попрактиковаться с [вопросами на подумать](https://zhukovsd.github.io/java-backend-interview-prep/tasks-to-think/) и [лайвкодингом](https://zhukovsd.github.io/java-backend-interview-prep/livecoding/). 39 | 40 | Для полноценного сопровождения поиска работы можете рассмотреть моё [менторство по трудоустройству](https://telegra.ph/Mentorstvo-po-trudoustrojstvu-10-26). 41 | 42 | ## Как поддержать 43 | 44 | - Пулл реквестами к [GitHub репозиторию](https://github.com/zhukovsd/java-backend-interview-prep/) методички с уточнениями ответов на теоретические вопросы 45 | - Активностью в [сообществе](https://t.me/zhukovsd_it_chat) 46 | - Материально - на [Boosty](https://boosty.to/zhukovsd) или через покупку [платных услуг и продуктов](https://t.me/zhukovsd_it_chat/143340/143341) 47 | -------------------------------------------------------------------------------- /content/livecoding.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Лайвкодинг' 3 | weight = 4 4 | bookFlatSection = true 5 | bookToC = false 6 | +++ 7 | 8 | # Лайвкодинг 9 | 10 | --- 11 | ### 1. [Java](Задачи/main-livecoding-java.md) 12 | 13 | --- 14 | ### 2. [SQL](Задачи/main-livecoding-sql.md) 15 | 16 | --- 17 | ### 3. [Stream API](Задачи/main-livecoding-stream.md) 18 | 19 | --- -------------------------------------------------------------------------------- /content/Архитектура и разработка/Spring/Spring_Bean_Life_cycle_Flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/content/Архитектура и разработка/Spring/Spring_Bean_Life_cycle_Flow.png -------------------------------------------------------------------------------- /content/Архитектура и разработка/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Архитектура и разработка' 3 | weight = 40 4 | bookFlatSection = true 5 | +++ 6 | 7 | -------------------------------------------------------------------------------- /content/Вопросы на подумать/Clean code, SOLID, patterns and refactoring.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Чистый код, SOLID, паттерны и рефакторинг' 3 | weight = 9 4 | bookFlatSection = false 5 | bookToC = false 6 | bookHidden = true 7 | +++ 8 | 9 | # Чистый код, SOLID, паттерны и рефакторинг 10 | 11 | --- 12 | ### 1. Есть один монолит на Java 11. Он содержит системы, которые надо вынести. С чего бы начал? 13 | ### 2. У нас есть SOLID и есть множественная имплементация. С одной стороны говорят дробите, с другой принцип единой ответственности, как решить этот диссонанс? 14 | ### 3. Метод, в котором 150 if-ов, где сравниваются строки. Как отрефакторить? 15 | ### 4. Сервис А должен сходить в Сервис Б. Они не знают API друг друга. Какой паттерн помогает им понять? 16 | ### 5. Планирование архитектуры между несколькими сервисами. (нужно смотреть фотографию) Как бы ты создал Контракт взаимодействия между сервисом egrn и внутренними клиентами. И между egrn и внешним сервисом Росреестр. 17 | ### 6. В первом сервисе произошло событие, закоммитили в БД, нужно отправить сообщение в кафку. Кафка реализует мост at least once т.е. может много сообщений в кафку. Если транзакция закоммителась мы должны гарантированно другому сервису сообщать, что событие произошло. Какие паттерны реализовать? 18 | ### 7. Общаемся с сервисом по REST, получили сетевую ошибку, кажется, что можно вызывать заново. Что настроить на своей стороне? - паттерн Retry - Если соседний сервис очень слабый, то постоянный retry еще больше его положит - увеличить промежутки времени между retry или паттерн circuit breaker -------------------------------------------------------------------------------- /content/Вопросы на подумать/Debugging and performance.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Отладка и производительность' 3 | weight = 8 4 | bookFlatSection = false 5 | bookToC = false 6 | bookHidden = true 7 | +++ 8 | 9 | # Отладка и производительность 10 | 11 | --- 12 | ### 1. Действия при OutOfMemory. Сервис перезагружается каждые несколько часов 13 | ### 2. ЦПУ улетел вверх. Твои действия. 14 | ### 3. Приходил запрос от саппорта. Клиент жалуется, что у него тормозит конкретная страница. Как будешь начинать копать задачу и куда будешь двигаться. 15 | ### 4. Есть микросервис. Есть ендпоинт. Ендпоинт не укладывается по времени работы на какой то ступени нагрузочного тестирования. Нужно ускорить его работу. Как приступить к этой задаче? Предположим у нас нет БД. Какие болевые точки могут быть? 16 | ### 5. Есть java сервер. Сервер завис. Как подойти к этой проблеме? -------------------------------------------------------------------------------- /content/Вопросы на подумать/Git and build systems.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Git и системы сборки' 3 | weight = 10 4 | bookFlatSection = false 5 | bookToC = false 6 | bookHidden = true 7 | +++ 8 | 9 | # Git и системы сборки 10 | 11 | --- 12 | ### 1. Есть Петя и Вася пишут каждый пишет в своей ветке, Пети понадобился один из комитов Васи как это сделать? 13 | ### 2. Есть maven репозиторий с API и репозиторий с вашим сервисом который подключен как зависимость, после обновления API и сервиса - обновленния не произошло, почему? Загрузиться ли в репозиторий изменения при использовании package? 14 | ### 3. Как в консоле скомпилировать и запустить класс? -------------------------------------------------------------------------------- /content/Вопросы на подумать/Team processes.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Командные процессы' 3 | weight = 11 4 | bookFlatSection = true 5 | bookToC = false 6 | bookHidden = true 7 | +++ 8 | 9 | # Командные процессы 10 | 11 | --- 12 | ### 1. Ты делаешь задачу и не успеваешь к дедлайну, какие твои действия? 13 | ### 2. Сервис интеграции с платежным шлюзом. Два метода. Как бы вы оценили эту таску и какой срок реализации таски? 14 | ### 3. Есть куб. Он состоит из тысячи маленьких кубиков. То есть у нас куб со стороной 10 на 10 на 10. Нужно найти количество кубиков, находящихся на внешнем слое этого куба. То есть мы можем крутить-вертеть, сколько мы можем в сумме увидеть на внешнем слое кубиков -------------------------------------------------------------------------------- /content/Вопросы на подумать/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'temp' 3 | weight = 99 4 | bookFlatSection = false 5 | bookToC = false 6 | bookHidden = true 7 | +++ -------------------------------------------------------------------------------- /content/Задачи/_index.md: -------------------------------------------------------------------------------- 1 | --- 2 | bookHidden: true 3 | --- -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-check-palindrome.md: -------------------------------------------------------------------------------- 1 | #### 2. Проверить, является ли строка палиндромом? 2 | 3 | 4 | ``` 5 | "A man, a plan, a canal, Panama!" 6 | 7 | ``` 8 | 9 | {{< hint warning >}} 10 | **Спойлеры к решению**   11 | {{< /hint >}} 12 | 13 | {{< details "Подсказки" close >}} 14 | - Палиндром — это строка, которая читается одинаково как слева направо, так и справа налево. 15 | - Нужно учесть, что пробелы, знаки препинания и регистр символов не имеют значения при проверке. 16 | - Можно использовать два указателя: один с начала строки, другой с конца, и проверять, совпадают ли символы, игнорируя пробелы и знаки препинания. 17 | {{< /details >}} 18 | 19 | {{< details "Решение" close >}} 20 | Для решения задачи можно использовать регулярные выражения для удаления всего, что не является буквами или цифрами, а затем сравнить строку с её обратной версией. 21 | 22 | ```java 23 | class Main { 24 | public static void main(String[] args) { 25 | String text = "A man, a plan, a canal, Panama!"; 26 | 27 | // Преобразуем строку в нижний регистр и удаляем все ненужные символы 28 | String cleanedText = text.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); 29 | 30 | // Проверяем, равна ли строка своему обратному представлению 31 | if (isPalindrome(cleanedText)) { 32 | System.out.println("Строка является палиндромом"); 33 | } else { 34 | System.out.println("Строка не является палиндромом"); 35 | } 36 | } 37 | 38 | // Метод для проверки, является ли строка палиндромом 39 | public static boolean isPalindrome(String text) { 40 | int left = 0; 41 | int right = text.length() - 1; 42 | 43 | while (left < right) { 44 | if (text.charAt(left) != text.charAt(right)) { 45 | return false; 46 | } 47 | left++; 48 | right--; 49 | } 50 | return true; 51 | } 52 | } 53 | ``` 54 | 55 | **Что делает код:** 56 | 57 | 1. Мы убираем все символы, которые не являются буквами или цифрами, с помощью регулярного выражения `replaceAll("[^a-zA-Z0-9]", "")`. 58 | 2. Преобразуем строку в нижний регистр, чтобы игнорировать регистр. 59 | 3. Метод `isPalindrome` проверяет, совпадают ли символы с обеих сторон строки (сначала и с конца). 60 | 61 | **Результат для строки `"A man, a plan, a canal, Panama!"`:** 62 | 63 | ``` 64 | Строка является палиндромом 65 | ``` 66 | 67 | Палиндром проверен успешно, так как после очистки и приведения к единому регистру строка становится `"amanaplanacanalpanama"`, которая читается одинаково в обоих направлениях. ✅ 68 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-find-first-unique-element.md: -------------------------------------------------------------------------------- 1 | #### 1. Найти первый неповторяющийся элемент в массиве 2 | 3 | ```java 4 | public static void main(String[] args) { 5 | int[] nums = {4, 5, 1, 2, 0, 4, 5, 2}; 6 | 7 | ``` 8 | 9 | {{< hint warning >}} 10 | **Спойлеры к решению**   11 | {{< /hint >}} 12 | 13 | {{< details "Подсказки" close >}} 14 | - Можно использовать `LinkedHashMap`, так как он сохраняет порядок вставки элементов и позволяет эффективно отслеживать количество вхождений. 15 | - Перебираем массив и заносим элементы в `Map`, увеличивая счетчик повторений. 16 | - Затем снова проходим по массиву и находим первый элемент с `count == 1`. 17 | {{< /details >}} 18 | 19 | {{< details "Решение" close >}} 20 | 21 | ```java 22 | import java.util.LinkedHashMap; 23 | import java.util.Map; 24 | 25 | public class FirstUniqueElement { 26 | public static int findFirstUnique(int[] nums) { 27 | Map countMap = new LinkedHashMap<>(); 28 | 29 | for (int num : nums) { 30 | countMap.put(num, countMap.getOrDefault(num, 0) + 1); 31 | } 32 | 33 | for (int num : nums) { 34 | if (countMap.get(num) == 1) { 35 | return num; 36 | } 37 | } 38 | 39 | return -1; // Если уникального элемента нет 40 | } 41 | 42 | public static void main(String[] args) { 43 | int[] nums = {4, 5, 1, 2, 0, 4, 5, 2}; 44 | System.out.println(findFirstUnique(nums)); // 1 45 | } 46 | } 47 | ``` 48 | 49 | Вывод: 50 | 51 | ``` 52 | 1 53 | ``` 54 | 55 | Метод работает за `O(n)`, так как мы дважды проходим по массиву. ✅ 56 | {{< /details >}} 57 | 58 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-find-person-by-name.md: -------------------------------------------------------------------------------- 1 | #### 14. Написать реализацию метода findPersonByName(). Из списка persons найти человека с именем name 2 | 3 | ```java 4 | class Person { 5 | String name; 6 | Integer age; 7 | } 8 | 9 | Optional findPersonByName(List persons, String name) { 10 | //... 11 | } 12 | 13 | ``` 14 | 15 | 16 | {{< hint warning >}} 17 | **Спойлеры к решению**   18 | {{< /hint >}} 19 | 20 | {{< details "Подсказки" close >}} 21 | 🔹 Нам нужно найти объект `Person`, у которого `name` совпадает с переданным. 22 | 🔹 Лучше вернуть `Optional`, чтобы избежать `null`. 23 | 🔹 Можно использовать `stream()` и `filter()`, либо традиционный `for`-цикл. 24 | {{< /details >}} 25 | 26 | {{< details "Решение" close >}} 27 | 28 | ✅ **С использованием `stream()` (современный вариант)** 29 | 30 | ```java 31 | import java.util.List; 32 | import java.util.Optional; 33 | 34 | public class PersonFinder { 35 | public static Optional findPersonByName(List persons, String name) { 36 | return persons.stream() 37 | .filter(person -> person.name.equals(name)) 38 | .findFirst(); 39 | } 40 | } 41 | ``` 42 | 43 | 📌 **Объяснение:** 44 | 45 | - `stream()` создает поток элементов списка. 46 | - `filter()` пропускает только тех, у кого `name` совпадает. 47 | - `findFirst()` берет первый найденный элемент. 48 | - Используем `Optional`, чтобы избежать `null`. 49 | 50 | ✅ **С использованием `for`-цикла (традиционный вариант)** 51 | 52 | ```java 53 | public static Optional findPersonByName(List persons, String name) { 54 | for (Person person : persons) { 55 | if (person.name.equals(name)) { 56 | return Optional.of(person); 57 | } 58 | } 59 | return Optional.empty(); 60 | } 61 | ``` 62 | 63 | 📌 **Объяснение:** 64 | 65 | - Пробегаем список `persons`. 66 | - Если находим совпадение, оборачиваем результат в `Optional.of()`. 67 | - Если ничего не найдено – возвращаем `Optional.empty()`. 68 | 69 | 🔥 **Какой вариант лучше?** 70 | ✅ `stream()` – элегантный и читаемый, но может быть немного медленнее из-за создания потока. 71 | ✅ `for`-цикл – быстрее для небольших списков и понятен новичкам. 72 | 73 | Выбор зависит от контекста 🚀 74 | {{< /details >}} 75 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-find-two-sum-sorted.md: -------------------------------------------------------------------------------- 1 | #### 3. Найти 2 элемента упорядоченного массива, сумма которых равна заданному числу 2 | 3 | {{< hint warning >}} 4 | **Спойлеры к решению**   5 | {{< /hint >}} 6 | 7 | {{< details "Подсказки" close >}} 8 | - Так как массив **упорядоченный**, можно использовать два указателя: один в начале, другой в конце. 9 | - Если сумма элементов больше целевого числа, уменьшаем правый указатель. 10 | - Если сумма меньше, увеличиваем левый указатель. 11 | - Если сумма совпала — нашли ответ. 12 | - Если указатели пересеклись — таких элементов нет. 13 | - Временная сложность: **O(n)**, так как массив проходит максимум один раз. 14 | {{< /details >}} 15 | 16 | {{< details "Решение" close >}} 17 | 18 | ```java 19 | public class TwoSumSorted { 20 | public static int[] findTwoSum(int[] nums, int target) { 21 | int left = 0, right = nums.length - 1; 22 | 23 | while (left < right) { 24 | int sum = nums[left] + nums[right]; 25 | 26 | if (sum == target) { 27 | return new int[]{nums[left], nums[right]}; 28 | } else if (sum < target) { 29 | left++; // Увеличиваем левый индекс, чтобы увеличить сумму 30 | } else { 31 | right--; // Уменьшаем правый индекс, чтобы уменьшить сумму 32 | } 33 | } 34 | 35 | return new int[]{}; // Если ничего не нашли 36 | } 37 | 38 | public static void main(String[] args) { 39 | int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9}; 40 | int target = 10; 41 | int[] result = findTwoSum(nums, target); 42 | 43 | if (result.length > 0) { 44 | System.out.println("Найденные элементы: " + result[0] + " и " + result[1]); 45 | } else { 46 | System.out.println("Пара не найдена"); 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | **Пример работы:** 53 | Вход: `{1, 2, 3, 4, 5, 6, 7, 8, 9}`, `target = 10` 54 | Выход: `1 и 9` (или `2 и 8`, `3 и 7`, `4 и 6` в зависимости от того, какую пару он найдет первой). 55 | {{< /details >}} 56 | 57 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-find-two-sum-unsorted.md: -------------------------------------------------------------------------------- 1 | #### 4. Найти два элемента в неупорядоченном массиве, сумма которых равна заданному числу? 2 | 3 | {{< hint warning >}} 4 | **Спойлеры к решению**   5 | {{< /hint >}} 6 | 7 | {{< details "Подсказки" close >}} 8 | - Для **неупорядоченного** массива мы не можем использовать бинарный поиск. 9 | - **Оптимальный подход** — использовать `HashMap`, сохраняя разницу `target - num`. 10 | - Можно также использовать `Set`, чтобы проверять, встречался ли уже нужный элемент. 11 | - **Менее эффективный** способ — два вложенных цикла (`O(n^2)`). 12 | {{< /details >}} 13 | 14 | {{< details "Решение" close >}} 15 | 16 | ```java 17 | import java.util.*; 18 | 19 | public class TwoSum { 20 | public static int[] findTwoSum(int[] nums, int target) { 21 | Map map = new HashMap<>(); // Число -> Индекс 22 | for (int i = 0; i < nums.length; i++) { 23 | int complement = target - nums[i]; // Число, которое нужно найти 24 | if (map.containsKey(complement)) { 25 | return new int[]{map.get(complement), i}; // Найденные индексы 26 | } 27 | map.put(nums[i], i); // Добавляем число в map 28 | } 29 | return new int[]{}; // Если пары не найдено 30 | } 31 | 32 | public static void main(String[] args) { 33 | int[] nums = {4, 7, 1, -3, 2}; 34 | int target = 5; 35 | int[] result = findTwoSum(nums, target); 36 | if (result.length == 2) { 37 | System.out.println("Индексы: " + result[0] + ", " + result[1]); 38 | } else { 39 | System.out.println("Пара не найдена"); 40 | } 41 | } 42 | } 43 | ``` 44 | 45 | **Разбор кода:** 46 | 47 | - Используем `HashMap`, чтобы хранить число и его индекс. 48 | - Для каждого элемента вычисляем `complement = target - num`. 49 | - Если `complement` уже в `map`, значит нашли пару. 50 | - Время выполнения: **`O(n)`** (проходим массив один раз). 51 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-game-framework-refactoring.md: -------------------------------------------------------------------------------- 1 | #### 10. Дан проект. Все недочеты кода надо править. На основании данного кода нужно создать фреймворк игрового 2D мира, то есть нарастить код 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | Пишем проект Симуляция 10 | {{< /details >}} 11 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-http-get-contract.md: -------------------------------------------------------------------------------- 1 | #### 12. Спроектировать http запрос который возвращает договор по номеру 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | 🔹 Нужно создать **REST-контроллер** с эндпоинтом `GET /contract/{number}`. 10 | 🔹 Договор должен извлекаться из **сервиса** или **репозитория**. 11 | 🔹 Используем `@RestController` и `@GetMapping`. 12 | {{< /details >}} 13 | 14 | {{< details "Решение" close >}} 15 | Пример простого Spring Boot REST API: 16 | 17 | ```java 18 | import org.springframework.web.bind.annotation.*; 19 | 20 | @RestController 21 | @RequestMapping("/contract") 22 | public class ContractController { 23 | private final ContractService contractService; 24 | 25 | public ContractController(ContractService contractService) { 26 | this.contractService = contractService; 27 | } 28 | 29 | @GetMapping("/{number}") 30 | public Contract getContract(@PathVariable String number) { 31 | return contractService.findByNumber(number); 32 | } 33 | } 34 | ``` 35 | 36 | 📌 **Сервис для поиска договора:** 37 | 38 | ```java 39 | import org.springframework.stereotype.Service; 40 | import java.util.Map; 41 | 42 | @Service 43 | public class ContractService { 44 | private final Map contractStorage = Map.of( 45 | "123", new Contract("123", "John Doe"), 46 | "456", new Contract("456", "Jane Doe") 47 | ); 48 | 49 | public Contract findByNumber(String number) { 50 | return contractStorage.getOrDefault(number, new Contract("N/A", "Not Found")); 51 | } 52 | } 53 | ``` 54 | 55 | 📌 **Модель данных:** 56 | 57 | ```java 58 | public class Contract { 59 | private String number; 60 | private String client; 61 | 62 | public Contract(String number, String client) { 63 | this.number = number; 64 | this.client = client; 65 | } 66 | 67 | public String getNumber() { return number; } 68 | public String getClient() { return client; } 69 | } 70 | ``` 71 | 72 | 📌 **Пример запроса:** 73 | 74 | ``` 75 | GET /contract/123 76 | ``` 77 | 78 | 📌 **Ответ:** 79 | 80 | ```json 81 | { 82 | "number": "123", 83 | "client": "John Doe" 84 | } 85 | ``` 86 | 87 | 🚀 **Вывод:** Мы создали **контроллер**, **сервис** и **модель**, реализовав простой REST API для поиска договора по номеру. 88 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-http-post-contract.md: -------------------------------------------------------------------------------- 1 | #### 13. Спроектировать http запрос который создает новый договор 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | 🔹 Нужно создать **POST-запрос** `/contract`, который принимает JSON с данными договора. 10 | 🔹 Используем `@PostMapping` и `@RequestBody`. 11 | 🔹 Данные должны сохраняться в **хранилище** (можно использовать `Map` как временную БД). 12 | 13 | {{< /details >}} 14 | 15 | {{< details "Решение" close >}} 16 | Пример REST API для создания договора: 17 | 18 | ```java 19 | import org.springframework.http.ResponseEntity; 20 | import org.springframework.web.bind.annotation.*; 21 | 22 | @RestController 23 | @RequestMapping("/contract") 24 | public class ContractController { 25 | private final ContractService contractService; 26 | 27 | public ContractController(ContractService contractService) { 28 | this.contractService = contractService; 29 | } 30 | 31 | @PostMapping 32 | public ResponseEntity createContract(@RequestBody Contract contract) { 33 | Contract savedContract = contractService.save(contract); 34 | return ResponseEntity.ok(savedContract); 35 | } 36 | } 37 | ``` 38 | 39 | 📌 **Сервис для сохранения договора:** 40 | 41 | ```java 42 | import org.springframework.stereotype.Service; 43 | import java.util.*; 44 | 45 | @Service 46 | public class ContractService { 47 | private final Map contractStorage = new HashMap<>(); 48 | 49 | public Contract save(Contract contract) { 50 | contractStorage.put(contract.getNumber(), contract); 51 | return contract; 52 | } 53 | } 54 | ``` 55 | 56 | 📌 **Модель данных:** 57 | 58 | ```java 59 | public class Contract { 60 | private String number; 61 | private String client; 62 | 63 | public Contract() {} // Для JSON сериализации 64 | 65 | public Contract(String number, String client) { 66 | this.number = number; 67 | this.client = client; 68 | } 69 | 70 | public String getNumber() { return number; } 71 | public String getClient() { return client; } 72 | } 73 | ``` 74 | 75 | 📌 **Пример запроса:** 76 | 77 | ``` 78 | POST /contract 79 | Content-Type: application/json 80 | ``` 81 | 82 | 📌 **Тело запроса:** 83 | 84 | ```json 85 | { 86 | "number": "789", 87 | "client": "Alice Smith" 88 | } 89 | ``` 90 | 91 | 📌 **Ответ:** 92 | 93 | ```json 94 | { 95 | "number": "789", 96 | "client": "Alice Smith" 97 | } 98 | ``` 99 | 100 | 🚀 **Вывод:** Мы создали **POST-запрос**, который принимает JSON, сохраняет данные в хранилище и возвращает сохраненный объект. 101 | {{< /details >}} 102 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-binary-tree.md: -------------------------------------------------------------------------------- 1 | #### 30. Реализация двоичного дерева 2 | 3 | **Условие задачи:** 4 | 🔥 Напишите простую реализацию **двоичного дерева**: 5 | 6 | - добавление элементов 7 | 8 | - обход в **in-order** (сначала левый, потом текущий, потом правый) 9 | 10 | 11 | 12 | {{< hint warning >}} 13 | **Спойлеры к решению** 14 | {{< /hint >}} 15 | 16 | {{< details "Подсказки" close >}} 17 | 🌳 **Двоичное дерево поиска** (Binary Search Tree) хранит меньшие значения слева, большие — справа. 18 | 📌 Для вставки используем рекурсивный метод `insertRec`. 19 | 📌 Обход in-order позволяет получить **отсортированную последовательность** значений. 20 | 💡 Используй `System.out.print()` внутри рекурсивного обхода, чтобы увидеть структуру. 21 | {{< /details >}} 22 | 23 | {{< details "Решение" close >}} 24 | 25 | ```java 26 | public class BinaryTree { 27 | 28 | static class Node { 29 | int value; 30 | Node left; 31 | Node right; 32 | 33 | Node(int value) { 34 | this.value = value; 35 | } 36 | } 37 | 38 | private Node root; 39 | 40 | public void insert(int value) { 41 | root = insertRec(root, value); 42 | } 43 | 44 | private Node insertRec(Node root, int value) { 45 | if (root == null) return new Node(value); 46 | 47 | if (value < root.value) root.left = insertRec(root.left, value); 48 | else if (value > root.value) root.right = insertRec(root.right, value); 49 | 50 | return root; 51 | } 52 | 53 | public void inOrderTraversal() { 54 | inOrderRec(root); 55 | } 56 | 57 | private void inOrderRec(Node root) { 58 | if (root != null) { 59 | inOrderRec(root.left); 60 | System.out.print(root.value + " "); 61 | inOrderRec(root.right); 62 | } 63 | } 64 | 65 | public static void main(String[] args) { 66 | BinaryTree tree = new BinaryTree(); 67 | tree.insert(5); 68 | tree.insert(3); 69 | tree.insert(7); 70 | tree.insert(1); 71 | tree.insert(4); 72 | 73 | System.out.println("In-order traversal:"); 74 | tree.inOrderTraversal(); // Output: 1 3 4 5 7 75 | } 76 | } 77 | ``` 78 | 79 | 📌 **Что ты реализовал:** 80 | ✅ Добавление элементов в бинарное дерево. 81 | ✅ Обход in-order (слева → текущий → справа). 82 | ✅ Рекурсивную структуру дерева. 83 | ✅ Пример работы — можно запустить и проверить. 84 | 85 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-crud-books.md: -------------------------------------------------------------------------------- 1 | #### 26. CRUD для сущностей "Книги" и "Авторы" 2 | 3 | **Условие задачи:** 4 | 5 | 6 | Опишите CRUD операции для сущностей, используя формат "GET /some/path 200;". 7 | Нужно реализовать следующие операции для сущности книги (BookEntity) с учётом таблиц: books, authors, books_authors: 8 | 9 | 1. Получение всех книг 10 | 2. Получение конкретной книги 11 | 3. Обновление книги 12 | 4. Создание книги 13 | 5. Удаление книги 14 | 15 | 16 | 17 | {{< hint warning >}} 18 | **Спойлеры к решению**   19 | {{< /hint >}} 20 | 21 | {{< details "Подсказки" close >}} 22 | 💡 Используйте принципы REST для формирования эндпоинтов. 23 | 💡 Подбирайте соответствующие HTTP методы для каждой операции: GET для получения, POST для создания, PUT для обновления и DELETE для удаления. 24 | {{< /details >}} 25 | 26 | {{< details "Решение" close >}} 27 | ```plaintext 28 | 1. Получение всех книг: GET /books 29 | // Этот эндпоинт возвращает список всех книг из таблицы "books". 30 | 31 | 2. Получение конкретной книги: GET /books/{id} 32 | // Эндпоинт для получения данных конкретной книги по уникальному идентификатору {id}. 33 | 34 | 3. Обновление книги: PUT /books/{id} 35 | // Используется для обновления информации о книге. Клиент отправляет новые данные, и сервер возвращает обновлённую книгу. 36 | 37 | 4. Создание книги: POST /books 38 | // Эндпоинт для создания новой книги. При успешном создании возвращается статус 201 (Created). 39 | 40 | 5. Удаление книги: DELETE /books/{id} 41 | // Эндпоинт для удаления книги по {id}. Возвращает статус 204 (No Content) при успешном удалении. 42 | ``` 43 | 44 | Эти CRUD операции можно аналогичным образом определить и для сущности автора (AuthorEntity), если потребуется. 😊 45 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-fib-refactor.md: -------------------------------------------------------------------------------- 1 | #### 31. Рефакторинг функции Фибоначчи 2 | 3 | **Условие задачи:** 4 | 🔥 Проведи рефакторинг функции вычисления числа Фибоначчи. 5 | В оригинальном коде используется рекурсия без оптимизации, что приводит к экспоненциальной сложности. 6 | 7 | **Код:** 8 | 9 | ```java 10 | int fib(int n) { 11 | if (n <= 1) return n; 12 | return fib(n-1) + fib(n-2); 13 | } 14 | ```` 15 | 16 | 17 | {{< hint warning >}} **Спойлеры к решению** 18 | {{< /hint >}} 19 | 20 | {{< details "Подсказки" close >}} 21 | 💡 **Оригинальная сложность:** Рекурсивное вычисление без мемоизации имеет экспоненциальную сложность O(2^n). 22 | 💡 **Оптимизация:** Используй итеративное вычисление или мемоизацию (динамическое программирование), чтобы снизить сложность до O(n). 23 | 💡 **Итеративный подход:** Применение цикла для последовательного вычисления чисел Фибоначчи – эффективное решение для данной задачи. {{< /details >}} 24 | 25 | {{< details "Решение" close >}} 26 | 27 | ```java 28 | public class Fibonacci { 29 | 30 | // Итеративная реализация с линейной сложностью O(n) 31 | public static int fib(int n) { 32 | if (n <= 1) return n; 33 | int a = 0, b = 1; 34 | for (int i = 2; i <= n; i++) { 35 | int next = a + b; 36 | a = b; 37 | b = next; 38 | } 39 | return b; 40 | } 41 | 42 | public static void main(String[] args) { 43 | System.out.println("fib(10) = " + fib(10)); // Пример: 55 44 | } 45 | } 46 | ``` 47 | 48 | 📌 **Что улучшилось?** 49 | ✅ **Снижение сложности:** Итеративный подход снижает асимптотику с экспоненциальной до O(n). 50 | ✅ **Экономия памяти:** Использование двух переменных для хранения предыдущих значений вместо рекурсивного стека. 51 | ✅ **Повышенная производительность:** Новый подход позволяет эффективно вычислять Fibonacci для больших значений `n`. {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-fibonacci-sequence.md: -------------------------------------------------------------------------------- 1 | #### 21. Генерация ряда Фибоначчи 2 | 3 | 4 | **Условие задачи:** 5 | 🔢 **Реализуй метод, который принимает число `n` и возвращает ряд Фибоначчи длиной `n`**. 6 | 7 | 📌 **Пример:** 8 | 9 | ```java 10 | Input: 7 11 | Output: [0, 1, 1, 2, 3, 5, 8] 12 | ``` 13 | 14 | 15 | 16 | 17 | 18 | {{< hint warning >}} 19 | **Спойлеры к решению**   20 | {{< /hint >}} 21 | 22 | {{< details "Подсказки" close >}} 23 | 💡 **Ряд Фибоначчи** – это последовательность чисел, где каждое число равно сумме двух предыдущих: 24 | `0, 1, 1, 2, 3, 5, 8, 13, ...` 25 | 💡 Для решения можно использовать **итеративный** или **рекурсивный** подход. 26 | 💡 Оптимальный вариант – использовать **динамическое программирование** или **итерацию**. 27 | {{< /details >}} 28 | 29 | {{< details "Решение" close >}} 30 | 31 | 📌 **1. Итеративный способ (быстрее, не требует много памяти)** 32 | 33 | ```java 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | public class Fibonacci { 38 | public static List generateFibonacci(int n) { 39 | List sequence = new ArrayList<>(); 40 | if (n <= 0) return sequence; 41 | 42 | sequence.add(0); 43 | if (n == 1) return sequence; 44 | 45 | sequence.add(1); 46 | for (int i = 2; i < n; i++) { 47 | sequence.add(sequence.get(i - 1) + sequence.get(i - 2)); 48 | } 49 | return sequence; 50 | } 51 | 52 | public static void main(String[] args) { 53 | System.out.println(generateFibonacci(7)); 54 | } 55 | } 56 | ``` 57 | 58 | 💨 **Сложность алгоритма:** O(n) 59 | 60 | --- 61 | 62 | 📌 **2. Рекурсивный способ (медленнее, но красиво)** 63 | 64 | ```java 65 | import java.util.ArrayList; 66 | import java.util.List; 67 | 68 | public class FibonacciRecursive { 69 | public static List generateFibonacci(int n) { 70 | List sequence = new ArrayList<>(); 71 | for (int i = 0; i < n; i++) { 72 | sequence.add(fib(i)); 73 | } 74 | return sequence; 75 | } 76 | 77 | private static int fib(int n) { 78 | if (n <= 1) return n; 79 | return fib(n - 1) + fib(n - 2); 80 | } 81 | 82 | public static void main(String[] args) { 83 | System.out.println(generateFibonacci(7)); 84 | } 85 | } 86 | ``` 87 | 88 | ⚠ **Минус:** экспоненциальная сложность O(2ⁿ), медленно для больших `n`. 89 | 90 | --- 91 | 92 | 📌 **3. Оптимизированный рекурсивный способ (через мемоизацию)** 93 | 94 | ```java 95 | import java.util.HashMap; 96 | import java.util.Map; 97 | 98 | public class FibonacciMemoization { 99 | private static final Map memo = new HashMap<>(); 100 | 101 | public static int fib(int n) { 102 | if (n <= 1) return n; 103 | if (memo.containsKey(n)) return memo.get(n); 104 | int result = fib(n - 1) + fib(n - 2); 105 | memo.put(n, result); 106 | return result; 107 | } 108 | 109 | public static void main(String[] args) { 110 | System.out.println(fib(7)); 111 | } 112 | } 113 | ``` 114 | 115 | ⚡ **Плюс:** сложность O(n), запоминает вычисленные значения. 116 | 117 | --- 118 | 119 | 🚀 **Вывод:** 120 | ✅ Итеративный способ – лучший по производительности. 121 | ✅ Рекурсия – красива, но требует оптимизации (мемоизация). 122 | ✅ Теперь ты умеешь генерировать ряд Фибоначчи разными способами! 🔥 123 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-filter-users-in-place.md: -------------------------------------------------------------------------------- 1 | #### 37. Фильтрация списка пользователей in-place 2 | 3 | **Условие задачи:** 4 | 🔥 Реализовать метод `filterUsers`, который **in-place** отфильтровывает список `users`, оставляя только тех, у кого имя начинается с `namePrefix` и возраст больше 18. 5 | 6 | ```java 7 | import lombok.Data; 8 | import java.util.ArrayList; 9 | 10 | @Data 11 | class User { 12 | private String name; 13 | private int age; 14 | } 15 | 16 | public class UserUtils { 17 | public static void filterUsers(ArrayList users, String namePrefix) { 18 | // ваш код 19 | } 20 | } 21 | 22 | ``` 23 | 24 | {{< hint warning >}} 25 | Спойлеры к решению 26 | {{< /hint >}} 27 | 28 | {{< details "Подсказки" close >}} 29 | 💡 Для удаления элементов in-place используйте Iterator и его метод remove(). 30 | 💡 Можно также воспользоваться методом List.removeIf(...) из Java 8. 31 | 💡 Для проверки префикса используйте String.startsWith(namePrefix). 32 | 💡 Условие фильтрации: user.getAge() > 18. 33 | {{< /details >}} 34 | 35 | {{< details "Решение" close >}} 36 | 37 | ```java 38 | 39 | import lombok.Data; 40 | import java.util.ArrayList; 41 | import java.util.Iterator; 42 | 43 | @Data 44 | class User { 45 | private String name; 46 | private int age; 47 | } 48 | 49 | public class UserUtils { 50 | public static void filterUsers(ArrayList users, String namePrefix) { 51 | Iterator iterator = users.iterator(); 52 | while (iterator.hasNext()) { 53 | User user = iterator.next(); 54 | if (!user.getName().startsWith(namePrefix) || user.getAge() <= 18) { 55 | iterator.remove(); 56 | } 57 | } 58 | } 59 | 60 | // Альтернативный вариант с removeIf: 61 | // public static void filterUsers(ArrayList users, String namePrefix) { 62 | // users.removeIf(user -> 63 | // !user.getName().startsWith(namePrefix) || user.getAge() <= 18 64 | // ); 65 | // } 66 | 67 | public static void main(String[] args) { 68 | ArrayList list = new ArrayList<>(); 69 | list.add(new User("Alice", 20)); 70 | list.add(new User("Bob", 17)); 71 | list.add(new User("Alex", 25)); 72 | list.add(new User("Anna", 18)); 73 | 74 | filterUsers(list, "A"); 75 | list.forEach(u -> System.out.println(u.getName() + " " + u.getAge())); 76 | // Вывод: 77 | // Alice 20 78 | // Alex 25 79 | } 80 | } 81 | {{< /details >}} 82 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-find-person-by-name.md: -------------------------------------------------------------------------------- 1 | #### 22. Поиск Person по имени 2 | 3 | **Условие задачи:** 4 | 🔍 **Реализовать метод `findPersonByName`, который ищет объект `Person` в списке по имени.** 5 | 📌 **Если человек найден – вернуть `Optional`, иначе вернуть `Optional.empty()`.** 6 | 7 | 📌 **Пример:** 8 | 9 | ```java 10 | 11 | class Person { 12 | String name; 13 | Integer age; 14 | } 15 | 16 | Optional findPersonByName(List persons, String name) { 17 | return null; 18 | } 19 | ``` 20 | 21 | 22 | {{< hint warning >}} 23 | **Спойлеры к решению**   24 | {{< /hint >}} 25 | 26 | {{< details "Подсказки" close >}} 27 | **Подсказки:** 28 | 💡 Используй `Optional` для обработки случая, когда человек не найден. 29 | 💡 Можно использовать `stream().filter().findFirst()`, `for-each` или `for`-цикл. 30 | 💡 `equalsIgnoreCase()` поможет искать без учета регистра. 31 | {{< /details >}} 32 | 33 | {{< details "Решение" close >}} 34 | 35 | **Решение:** 36 | 37 | 📌 **1. Через Stream API (кратко и элегантно)** 38 | 39 | ```java 40 | import java.util.List; 41 | import java.util.Optional; 42 | 43 | class Person { 44 | String name; 45 | Integer age; 46 | 47 | public Person(String name, Integer age) { 48 | this.name = name; 49 | this.age = age; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "Person{name='" + name + "', age=" + age + "}"; 55 | } 56 | } 57 | 58 | public class PersonFinder { 59 | public static Optional findPersonByName(List persons, String name) { 60 | return persons.stream() 61 | .filter(p -> p.name.equalsIgnoreCase(name)) 62 | .findFirst(); 63 | } 64 | 65 | public static void main(String[] args) { 66 | List persons = List.of( 67 | new Person("Alice", 25), 68 | new Person("Bob", 30) 69 | ); 70 | 71 | System.out.println(findPersonByName(persons, "Alice")); // Optional[Person{name='Alice', age=25}] 72 | System.out.println(findPersonByName(persons, "John")); // Optional.empty 73 | } 74 | } 75 | ``` 76 | 77 | ✅ **Сложность**: O(n) – в худшем случае перебираем весь список. 78 | 79 | --- 80 | 81 | 📌 **2. Через обычный `for`-цикл** 82 | 83 | ```java 84 | public static Optional findPersonByName(List persons, String name) { 85 | for (Person person : persons) { 86 | if (person.name.equalsIgnoreCase(name)) { 87 | return Optional.of(person); 88 | } 89 | } 90 | return Optional.empty(); 91 | } 92 | ``` 93 | 94 | 📌 **Когда использовать?** Если не хочется использовать `Stream API`. 95 | 96 | --- 97 | 98 | 🚀 **Вывод:** 99 | ✅ **Stream API** – лаконичное и удобное решение. 100 | ✅ **Цикл `for`** – классика, иногда читается проще. 101 | ✅ Теперь ты можешь быстро искать людей по имени! 🔥 102 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-first-non-repeating-element.md: -------------------------------------------------------------------------------- 1 | #### 38. Поиск первого неповторяющегося элемента в массиве 2 | 3 | **Условие задачи:** 4 | 🔥 Дан массив целых чисел `{9, 4, 9, 6, 6, 4, 5}`. Необходимо найти первый элемент, который в этом массиве встречается ровно один раз. 5 | 6 | **Код:** 7 | 8 | ```java 9 | public class Main { 10 | public static void main(String[] args) { 11 | int[] array = {9, 4, 9, 6, 6, 4, 5}; 12 | // TODO 13 | } 14 | } 15 | ```` 16 | 17 | {{< hint warning >}} 18 | **Спойлеры к решению** 19 | {{< /hint >}} 20 | 21 | {{< details "Подсказки" close >}} 22 | 💡 Используйте `LinkedHashMap`, чтобы сохранить порядок вставки и подсчитать количество вхождений каждого числа. 23 | 💡 Первый проход по массиву: для каждого элемента увеличивайте счётчик в карте. 24 | 💡 Второй проход: найдите в исходном массиве первый элемент с частотой `1` в карте. 25 | {{< /details >}} 26 | 27 | {{< details "Решение" close >}} 28 | 29 | ```java 30 | import java.util.LinkedHashMap; 31 | import java.util.Map; 32 | 33 | public class Main { 34 | public static void main(String[] args) { 35 | int[] array = {9, 4, 9, 6, 6, 4, 5}; 36 | 37 | // Подсчёт вхождений с сохранением порядка 38 | Map countMap = new LinkedHashMap<>(); 39 | for (int num : array) { 40 | countMap.put(num, countMap.getOrDefault(num, 0) + 1); 41 | } 42 | 43 | // Поиск первого уникального элемента 44 | int firstUnique = -1; 45 | for (int num : array) { 46 | if (countMap.get(num) == 1) { 47 | firstUnique = num; 48 | break; 49 | } 50 | } 51 | 52 | if (firstUnique != -1) { 53 | System.out.println("Первый неповторяющийся элемент: " + firstUnique); 54 | } else { 55 | System.out.println("Уникальных элементов не найдено"); 56 | } 57 | } 58 | } 59 | ``` 60 | 61 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-fix-message-building.md: -------------------------------------------------------------------------------- 1 | #### 25. Исправление проблем в коде 2 | 3 | **Условие задачи:** 4 | 5 | Исправьте проблемы в коде компонента, который строит строковое сообщение с перечислением ошибок. Необходимо улучшить производительность и исправить ошибки в строках. 🛠️ 6 | 7 | ```java 8 | @Component 9 | public class Task2 { 10 | public String buildAuditMessage(List errors) { 11 | String msg = ""В процессе обработки возникли следующие проблемы: \n""; 12 | for (String error : errors) { 13 | msg += error + ""\n""; 14 | } 15 | return msg; 16 | } 17 | } 18 | ``` 19 | 20 | 21 | {{< hint warning >}} 22 | **Спойлеры к решению**   23 | {{< /hint >}} 24 | 25 | {{< details "Подсказки" close >}} 26 | 💡 Для эффективной конкатенации строк в цикле используйте **StringBuilder**, а не `+`. ⚡ 27 | 💡 Убедитесь, что строковые литералы правильно оформлены в кавычках. ✍️ 28 | {{< /details >}} 29 | 30 | {{< details "Решение" close >}} 31 | 32 | ```java 33 | @Component 34 | public class Task2 { 35 | public String buildAuditMessage(List errors) { 36 | // Используем StringBuilder для более эффективной конкатенации строк 37 | StringBuilder msg = new StringBuilder("В процессе обработки возникли следующие проблемы:\n"); 38 | for (String error : errors) { 39 | msg.append(error).append("\n"); 40 | } 41 | return msg.toString(); 42 | } 43 | } 44 | ``` 45 | 46 | 1. **StringBuilder** — используется для более эффективной конкатенации строк. В цикле с обычной строкой (с помощью оператора `+`) создается много промежуточных объектов, что замедляет выполнение. 🏃‍♂️ 47 | 2. Ошибка с кавычками в исходном коде: лишние кавычки и неправильный синтаксис. В исправленном варианте строки правильно оформлены. 48 | 49 | Использование `StringBuilder` позволяет избежать лишней нагрузки на память и ускорить выполнение программы. 🚀 50 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-fix-spring-app-code-errors.md: -------------------------------------------------------------------------------- 1 | #### 28. Исправление ошибок Spring Boot приложения 2 | 3 | **Условие задачи:** 4 | 🔥 Исправить ошибки в коде Spring Boot. 5 | 6 | **Код:** 7 | 8 | ```java 9 | package com.home.app; 10 | 11 | import org.springframework.boot.SpringApplication; 12 | import org.springframework.boot.autoconfigure.SpringBootApplication; 13 | 14 | @SpringBootApplication 15 | public class App { 16 | public static void main(String[] args) { 17 | SpringApplication.run(App.class, args); 18 | } 19 | } 20 | 21 | package com.home.controller; 22 | 23 | import org.springframework.boot.SpringApplication; 24 | import org.springframework.boot.autoconfigure.SpringBootApplication; 25 | 26 | @Controller 27 | public class Controller { 28 | @GetMapping(""/"") 29 | public String test() { 30 | return ""test""; 31 | } 32 | } 33 | ``` 34 | 35 | {{< hint warning >}} 36 | **Спойлеры к решению** 37 | {{< /hint >}} 38 | 39 | {{< details "Подсказки" close >}} 40 | 💡 **Раздели классы по файлам:** Каждый файл должен содержать одно объявление package. 41 | 💡 **Правильные импорты:** Используй `org.springframework.stereotype.Controller` для `@Controller` и `org.springframework.web.bind.annotation.GetMapping` для `@GetMapping`. 42 | 💡 **Синтаксис строк:** Исправь строковый литерал в `@GetMapping("/")` – убедись, что кавычки используются корректно. 43 | 💡 **Проверь структуру проекта:** Файлы должны находиться в директориях, соответствующих их package. {{< /details >}} 44 | 45 | {{< details "Решение" close >}} 46 | 47 | _Создаем два отдельных файла:_ 48 | 49 | **1. Файл App.java в пакете com.home.app:** 50 | 51 | ```java 52 | package com.home.app; 53 | 54 | import org.springframework.boot.SpringApplication; 55 | import org.springframework.boot.autoconfigure.SpringBootApplication; 56 | 57 | @SpringBootApplication 58 | public class App { 59 | public static void main(String[] args) { 60 | SpringApplication.run(App.class, args); 61 | } 62 | } 63 | ``` 64 | 65 | **2. Файл Controller.java в пакете com.home.controller:** 66 | 67 | ```java 68 | package com.home.controller; 69 | 70 | import org.springframework.stereotype.Controller; 71 | import org.springframework.web.bind.annotation.GetMapping; 72 | 73 | @Controller 74 | public class Controller { 75 | @GetMapping("/") 76 | public String test() { 77 | return "test"; 78 | } 79 | } 80 | ``` 81 | 82 | 📌 **Что улучшилось?** 83 | ✅ **Структурирование кода:** Каждый класс находится в отдельном файле с корректной организацией пакетов. 84 | ✅ **Использование правильных импортов:** Аннотации `@Controller` и `@GetMapping` импортированы из правильных пакетов. 85 | ✅ **Правильный синтаксис строк:** Строковый литерал в аннотации `@GetMapping` оформлен корректно. 86 | ✅ **Соответствие стандартам Spring Boot приложения.** 87 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-min-stack.md: -------------------------------------------------------------------------------- 1 | #### 41. Реализация стека с поддержкой получения минимума за O(1) 2 | 3 | **Условие задачи:** 4 | 🔥 Написать класс `MinStack` с методами: 5 | - `void push(int x)` — добавить элемент в стек; 6 | - `int pop()` — удалить и вернуть верхний элемент стека; 7 | - `int top()` — вернуть верхний элемент без удаления; 8 | - `int peekMin()` — вернуть минимальный элемент в стеке за O(1). 9 | 10 | **Код:** 11 | 12 | ```java 13 | public class MinStack { 14 | // TODO 15 | } 16 | ``` 17 | 18 | {{< hint warning >}} 19 | **Спойлеры к решению** 20 | {{< /hint >}} 21 | 22 | {{< details "Подсказки" close >}} 23 | 💡 Используйте **два стека**: один для хранения всех элементов, другой — для хранения текущих минимумов. 24 | 💡 При `push(x)` добавляйте в стек-min либо `x`, если стек-min пуст, либо `min(x, текущий минимум)`. 25 | 💡 При `pop()` удаляйте элементы из обоих стеков одновременно. 26 | 💡 `peekMin()` просто возвращает верхний элемент из второго стека. 27 | {{< /details >}} 28 | 29 | {{< details "Решение" close >}} 30 | 31 | ```java 32 | import java.util.Stack; 33 | 34 | public class MinStack { 35 | private final Stack stack; 36 | private final Stack minStack; 37 | 38 | public MinStack() { 39 | stack = new Stack<>(); 40 | minStack = new Stack<>(); 41 | } 42 | 43 | public void push(int x) { 44 | stack.push(x); 45 | // Если стек мин пуст или новый элемент меньше текущего минимума — пушим его 46 | if (minStack.isEmpty() || x <= minStack.peek()) { 47 | minStack.push(x); 48 | } else { 49 | // Иначе дублируем текущий минимум 50 | minStack.push(minStack.peek()); 51 | } 52 | } 53 | 54 | public int pop() { 55 | if (stack.isEmpty()) { 56 | throw new RuntimeException("Стек пуст"); 57 | } 58 | minStack.pop(); 59 | return stack.pop(); 60 | } 61 | 62 | public int top() { 63 | if (stack.isEmpty()) { 64 | throw new RuntimeException("Стек пуст"); 65 | } 66 | return stack.peek(); 67 | } 68 | 69 | public int peekMin() { 70 | if (minStack.isEmpty()) { 71 | throw new RuntimeException("Стек пуст"); 72 | } 73 | return minStack.peek(); 74 | } 75 | 76 | public static void main(String[] args) { 77 | MinStack ms = new MinStack(); 78 | ms.push(5); 79 | ms.push(2); 80 | ms.push(4); 81 | System.out.println("Мин: " + ms.peekMin()); // 2 82 | ms.push(1); 83 | System.out.println("Мин: " + ms.peekMin()); // 1 84 | ms.pop(); 85 | System.out.println("Мин: " + ms.peekMin()); // 2 86 | System.out.println("Top: " + ms.top()); // 4 87 | } 88 | } 89 | ``` 90 | 91 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-point-hashcode.md: -------------------------------------------------------------------------------- 1 | #### 40. Реализация `hashCode` для класса Point 2 | 3 | **Условие задачи:** 4 | 📌 Реализовать метод `hashCode()` для класса `Point` с полями `int x` и `int y`. Объяснить, что возвращает `hashCode()`. 5 | 6 | **Код:** 7 | 8 | ```java 9 | public class Point { 10 | private int x; 11 | private int y; 12 | 13 | // конструкторы, геттеры/сеттеры 14 | 15 | @Override 16 | public int hashCode() { 17 | // TODO: ваша реализация 18 | } 19 | } 20 | ``` 21 | 22 | 23 | {{< hint warning >}} 24 | **Спойлеры к решению** 25 | {{< /hint >}} 26 | 27 | {{< details "Подсказки" close >}} 28 | 💡 Для смешивания полей используйте **простое умножение на простое число** (часто 31). 29 | 💡 Начальная величина результата может быть, например, `17` или `x`. 30 | 💡 Формула: 31 | 32 | ```java 33 | int result = 17; 34 | result = 31 * result + x; 35 | result = 31 * result + y; 36 | return result; 37 | ``` 38 | 39 | 💡 Можно воспользоваться утилитой `Objects.hash(x, y)` для краткости, но лучше понять классическое решение. 40 | {{< /details >}} 41 | 42 | {{< details "Решение" close >}} 43 | 44 | ```java 45 | import java.util.Objects; 46 | 47 | public class Point { 48 | private int x; 49 | private int y; 50 | 51 | // Конструктор, геттеры/сеттеры omitted for brevity 52 | 53 | @Override 54 | public int hashCode() { 55 | int result = 17; // начальное произвольное ненулевое значение 56 | result = 31 * result + x; // учитываем поле x 57 | result = 31 * result + y; // учитываем поле y 58 | return result; 59 | } 60 | 61 | @Override 62 | public boolean equals(Object o) { 63 | if (this == o) return true; 64 | if (!(o instanceof Point)) return false; 65 | Point other = (Point) o; 66 | return this.x == other.x && this.y == other.y; 67 | } 68 | } 69 | ``` 70 | 71 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-printformattedtext.md: -------------------------------------------------------------------------------- 1 | #### 18. Форматирование текста в консоли 2 | 3 | **Условие задачи:** 4 | Напиши метод, который принимает строку и выводит на консоль отформатированный текст в виде рамки. В рамках должна быть отображена каждая часть строки в отдельной строке так, как показано в примере ниже. 5 | 😊 **Пример вывода:** 6 | 7 | ``` 8 | **************** 9 | * I * 10 | * am * 11 | * Java * 12 | * Developer * 13 | **************** 14 | ``` 15 | 16 | --- 17 | 18 | **Код:** 19 | 20 | ```java 21 | public class Task1 { 22 | private static final String TEXT = "I am Java Developer"; 23 | 24 | /* 25 | **************** 26 | * I * 27 | * am * 28 | * Java * 29 | * Developer * 30 | **************** 31 | */ 32 | 33 | /** 34 | * Реализовать функцию вывода на консоль текста в параметре TEXT в формате, указанном выше. 35 | */ 36 | public void printFormattedText(String text) { 37 | throw new UnsupportedOperationException("Implemented it!"); 38 | } 39 | } 40 | ``` 41 | 42 | --- 43 | 44 | {{< hint warning >}} 45 | **Спойлеры к решению**   46 | {{< /hint >}} 47 | 48 | {{< details "Подсказки" close >}} 49 | 50 | - **Разбей строку на слова:** 51 | Используй метод `split(" ")`, чтобы получить массив слов. 52 | - **Вычисли максимальную длину слова:** 53 | Это поможет правильно установить ширину рамки. 54 | - **Построй рамку:** 55 | Используй символ `*` для создания верхней и нижней границы. 56 | - **Форматированный вывод:** 57 | При выводе каждого слова используй форматирование, чтобы добавить пробелы до нужной длины. 58 | 59 | {{< /details >}} 60 | 61 | {{< details "Решение" close >}} 62 | 63 | ```java 64 | public class Task1 { 65 | private static final String TEXT = "I am Java Developer"; 66 | 67 | /** 68 | * Функция выводит текст, разбитый на слова, в виде рамки. 69 | * Пример: 70 | * **************** 71 | * * I * 72 | * * am * 73 | * * Java * 74 | * * Developer * 75 | * **************** 76 | */ 77 | public void printFormattedText(String text) { 78 | if (text == null || text.trim().isEmpty()) { 79 | System.out.println("Текст пустой!"); 80 | return; 81 | } 82 | 83 | // Разбиваем строку на слова 84 | String[] words = text.split("\\s+"); 85 | 86 | // Находим максимальную длину слова 87 | int maxLength = 0; 88 | for (String word : words) { 89 | if (word.length() > maxLength) { 90 | maxLength = word.length(); 91 | } 92 | } 93 | 94 | // Формируем верхнюю и нижнюю границы рамки 95 | String border = "*".repeat(maxLength + 6); 96 | 97 | // Вывод рамки 98 | System.out.println(border); 99 | for (String word : words) { 100 | // Форматируем вывод так, чтобы слово выводилось с отступами 101 | System.out.printf("* %-"+maxLength+"s *%n", word); 102 | } 103 | System.out.println(border); 104 | } 105 | 106 | public static void main(String[] args) { 107 | Task1 task = new Task1(); 108 | task.printFormattedText(TEXT); 109 | } 110 | } 111 | ``` 112 | 113 | {{< /details >}} 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-rename-attribute.md: -------------------------------------------------------------------------------- 1 | #### 27. [Изменение названия атрибута в API] 2 | 3 | **Условие задачи:** 4 | 5 | Организовать переход на изменение названия принимающего атрибута с **title** на **name**. Нужно обеспечить обратную совместимость, чтобы клиенты, использующие старый формат (`"title": "..."`), могли продолжать работать, а новые — использовать `"name": "..."`. 6 | 7 | Исходный JSON: 8 | 9 | ```json 10 | { 11 | "title": "..." 12 | } 13 | ``` 14 | 15 | Новый JSON: 16 | 17 | ```json 18 | { 19 | "name": "..." 20 | } 21 | ``` 22 | 23 | {{< hint warning >}} 24 | **Спойлеры к решению**   25 | {{< /hint >}} 26 | 27 | {{< details "Подсказки" close >}} 28 | 💡 Используйте аннотацию **@JsonAlias** для поддержки старого названия атрибута. 29 | 💡 Основное имя атрибута задается с помощью **@JsonProperty**. 30 | {{< /details >}} 31 | 32 | {{< details "Решение" close >}} 33 | 34 | ```java 35 | import com.fasterxml.jackson.annotation.JsonAlias; 36 | import com.fasterxml.jackson.annotation.JsonProperty; 37 | 38 | public class BookDTO { 39 | // Новое основное имя "name", поддерживаем также "title" 40 | @JsonProperty("name") 41 | @JsonAlias("title") 42 | private String name; 43 | 44 | // Геттер 45 | public String getName() { 46 | return name; 47 | } 48 | 49 | // Сеттер 50 | public void setName(String name) { 51 | this.name = name; 52 | } 53 | } 54 | ``` 55 | 56 | **Объяснение решения:** 57 | 58 | 1. **@JsonProperty("name")** 59 | Эта аннотация указывает, что основное имя атрибута при сериализации и десериализации — **name**. 📘 60 | 61 | 2. **@JsonAlias("title")** 62 | Позволяет принимать входящие JSON с полем **title**. Это обеспечивает обратную совместимость, и клиенты, использующие старое название, смогут продолжать отправлять данные в прежнем формате. 🔄 63 | 64 | 65 | Таким образом, используя комбинацию этих аннотаций, мы можем легко перевести API на новое имя атрибута, не ломая работу с уже существующими клиентами. 🚀 66 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-repeated-string.md: -------------------------------------------------------------------------------- 1 | #### 36. Решение задачи HackerRank "Repeat String" 2 | 3 | **Условие задачи:** 4 | 🔥 Дан строка `s`, состоящая из строчных английских букв, которая повторяется бесконечно. Дано целое число `n`. Необходимо посчитать количество букв `'a'` в первых `n` символах этой бесконечной строки. 5 | 6 | **Пример:** 7 | - s = "abcac", n = 10 8 | - Подстрока из первых 10 символов: "abcacabcac". 9 | - Количество букв 'a': 4 10 | 11 | 12 | ```java 13 | 14 | class Result { 15 | 16 | /* 17 | * Complete the 'repeatedString' function below. 18 | * 19 | * The function is expected to return a LONG_INTEGER. 20 | * The function accepts following parameters: 21 | * 1. STRING s 22 | * 2. LONG_INTEGER n 23 | */ 24 | } 25 | 26 | ``` 27 | 28 | 29 | 30 | {{< hint warning >}} **Спойлеры к решению** 31 | {{< /hint >}} 32 | 33 | {{< details "Подсказки" close >}} 34 | 💡 **Используй деление:** Определи число полных повторений строки, разделив `n` на длину строки `s`. 35 | 💡 **Подсчитай 'a':** Сначала посчитай количество `'a'` в строке `s`, а затем умножь на число полных повторений. 36 | 💡 **Обработка остатка:** Не забудь проверить первые `n % s.length()` символов для корректного подсчёта. {{< /details >}} 37 | 38 | {{< details "Решение" close >}} 39 | 40 | ```java 41 | public class Result { 42 | 43 | public static long repeatedString(String s, long n) { 44 | // Подсчёт количества 'a' в строке s 45 | long countA = 0; 46 | for (char c : s.toCharArray()) { 47 | if (c == 'a') { 48 | countA++; 49 | } 50 | } 51 | 52 | // Определяем, сколько полных повторов строки укладывается в n символов 53 | long fullRepeats = n / s.length(); 54 | long totalA = fullRepeats * countA; 55 | 56 | // Обработка остатка: считаем 'a' в первых (n % s.length()) символах 57 | int remainingChars = (int) (n % s.length()); 58 | for (int i = 0; i < remainingChars; i++) { 59 | if (s.charAt(i) == 'a') { 60 | totalA++; 61 | } 62 | } 63 | return totalA; 64 | } 65 | 66 | public static void main(String[] args) { 67 | String s = "abcac"; 68 | long n = 10; 69 | System.out.println("Количество 'a': " + repeatedString(s, n)); // Ожидаемый вывод: 4 70 | } 71 | } 72 | ``` 73 | 74 | 📌 **Что улучшилось?** 75 | ✅ **Оптимальность:** Вместо создания бесконечной строки алгоритм учитывает полные повторения и остаток, что эффективно при больших `n`. 76 | ✅ **Читаемость кода:** Ясное разделение логики на подсчёт `a` в полном повторении и обработку остатка. 77 | ✅ **Стабильность:** Решение работает корректно даже для очень больших значений `n`. {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-reverse-number.md: -------------------------------------------------------------------------------- 1 | #### 32. Инверсия числа 2 | 3 | **Условие задачи:** 4 | 🔥 Написать метод, который принимает целое число, инвертирует его цифры и возвращает инвертированное число. 5 | - Примеры: 6 | - Было: -123, стало: -321 7 | - Было: 1121, стало: 1211 8 | - Диапазон допустимых чисел: от -10000 до 10000. 9 | - Если число вне диапазона, сообщите об этом пользователю API. 10 | 11 | 12 | 13 | {{< hint warning >}} **Спойлеры к решению** 14 | {{< /hint >}} 15 | 16 | {{< details "Подсказки" close >}} 17 | 💡 **Диапазон:** Проверь диапазон входного числа до начала инверсии. 18 | 💡 **Инверсия цифр:** Используй цикл while, чтобы последовательно извлекать и собирать цифры числа. 19 | 💡 **Обработка знака:** Сохрани знак числа и применяй его после инверсии. 20 | 💡 **Отладка:** Добавь вывод сообщения, если число выходит за допустимый диапазон. {{< /details >}} 21 | 22 | {{< details "Решение" close >}} 23 | 24 | ```java 25 | public class NumberUtils { 26 | 27 | public static Integer reverse(int x) { 28 | // Проверка диапазона: число должно быть от -10000 до 10000 29 | if (x < -10000 || x > 10000) { 30 | System.err.println("Введённое число " + x + " выходит за пределы диапазона [-10000, 10000]."); 31 | return null; 32 | } 33 | 34 | int sign = x < 0 ? -1 : 1; 35 | int absX = Math.abs(x); 36 | int reversed = 0; 37 | 38 | // Инверсия цифр 39 | while (absX > 0) { 40 | reversed = reversed * 10 + absX % 10; 41 | absX /= 10; 42 | } 43 | 44 | return sign * reversed; 45 | } 46 | 47 | public static void main(String[] args) { 48 | int[] tests = {-123, 1121, 15000}; 49 | for (int num : tests) { 50 | Integer result = reverse(num); 51 | if (result != null) { 52 | System.out.println("Исходное число: " + num + ", инвертированное: " + result); 53 | } 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | 📌 **Что улучшилось?** 60 | ✅ **Проверка диапазона:** Метод обрабатывает входные данные вне допустимого диапазона. 61 | ✅ **Корректная инверсия:** Сохранение знака числа и последовательное извлечение цифр дают правильный результат. 62 | ✅ **Читаемость кода:** Лёгкая для понимания реализация, что упрощает поддержку. 63 | ✅ **Пример использования:** Функция main демонстрирует работу метода с различными входными данными. {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-reverse-string.md: -------------------------------------------------------------------------------- 1 | #### 39. Реверс строки 2 | 3 | **Условие задачи:** 4 | 📌 Напишите метод, который принимает строку и возвращает её реверс. 5 | 6 | **Код:** 7 | 8 | ```java 9 | public class StringUtils { 10 | public static String reverse(String input) { 11 | // TODO 12 | } 13 | } 14 | ```` 15 | 16 | 17 | {{< hint warning >}} 18 | **Спойлеры к решению** 19 | {{< /hint >}} 20 | 21 | {{< details "Подсказки" close >}} 22 | 💡 Проверьте вход на `null` и при необходимости выбросьте `IllegalArgumentException`. 23 | 💡 Используйте `StringBuilder(input).reverse().toString()` для простого и быстрого реверса. 24 | 💡 Альтернативно можно обойти строку с конца до начала и собирать символы в новый `StringBuilder`. 25 | {{< /details >}} 26 | 27 | {{< details "Решение" close >}} 28 | 29 | ```java 30 | public class StringUtils { 31 | public static String reverse(String input) { 32 | if (input == null) { 33 | throw new IllegalArgumentException("Input string must not be null"); 34 | } 35 | return new StringBuilder(input).reverse().toString(); 36 | } 37 | 38 | public static void main(String[] args) { 39 | System.out.println(reverse("hello")); // olleh 40 | System.out.println(reverse("Java")); // avaJ 41 | } 42 | } 43 | ``` 44 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-singleton.md: -------------------------------------------------------------------------------- 1 | #### 34. Реализация паттерна Singleton в Java 2 | 3 | 4 | **Условие задачи:** 5 | 🔥 Напишите реализацию паттерна Singleton в Java. 6 | 7 | 8 | 9 | {{< hint warning >}} **Спойлеры к решению** 10 | {{< /hint >}} 11 | 12 | {{< details "Подсказки" close >}} 13 | 💡 Используй **private конструктор**, чтобы предотвратить создание экземпляров класса извне. 14 | 💡 Применяй **double-checked locking** с ключевым словом `volatile` для обеспечения потокобезопасности и избежания избыточной синхронизации. 15 | 💡 Метод `getInstance()` возвращает единственный экземпляр класса, гарантируя создание объекта только при первом обращении. {{< /details >}} 16 | 17 | {{< details "Решение" close >}} 18 | 19 | ```java 20 | public class Singleton { 21 | // volatile гарантирует, что изменения переменной instance видны всем потокам 22 | private static volatile Singleton instance; 23 | 24 | // Приватный конструктор предотвращает создание экземпляров извне 25 | private Singleton() { 26 | } 27 | 28 | // Метод для доступа к единственному экземпляру с использованием double-checked locking 29 | public static Singleton getInstance() { 30 | if (instance == null) { // Первичная проверка без синхронизации 31 | synchronized (Singleton.class) { 32 | if (instance == null) { // Вторая проверка внутри синхронизированного блока 33 | instance = new Singleton(); 34 | } 35 | } 36 | } 37 | return instance; 38 | } 39 | 40 | // Пример метода, который демонстрирует работу с Singleton 41 | public void doSomething() { 42 | System.out.println("Singleton instance is working!"); 43 | } 44 | 45 | // Пример использования 46 | public static void main(String[] args) { 47 | Singleton singleton = Singleton.getInstance(); 48 | singleton.doSomething(); // Вывод: Singleton instance is working! 49 | } 50 | } 51 | ``` 52 | 53 | 📌 **Что улучшилось?** 54 | ✅ **Потокобезопасность:** Использование `volatile` и блока `synchronized` с двойной проверкой гарантирует корректную работу в многопоточной среде. 55 | ✅ **Контроль создания экземпляра:** Приватный конструктор предотвращает создание дополнительных экземпляров класса. 56 | ✅ **Эффективность:** Double-checked locking позволяет избежать блокировки при каждом вызове `getInstance()` после инициализации. {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-sort-array.md: -------------------------------------------------------------------------------- 1 | #### 29. Сортировка массива чисел 2 | 3 | 4 | **Условие задачи:** 5 | 🔥 Напишите функцию, которая принимает массив `int[]`, сортирует его по возрастанию и возвращает результат. 6 | 7 | 8 | {{< hint warning >}} 9 | **Спойлеры к решению** 10 | {{< /hint >}} 11 | 12 | {{< details "Подсказки" close >}} 13 | 💡 Используй **алгоритм пузырьковой сортировки (Bubble Sort)**. 14 | 💡 Сравнивай соседние элементы и меняй местами, если они стоят не в том порядке. 15 | 💡 Повторяй процесс, пока массив не отсортируется. 16 | 💡 Сложность такого алгоритма — O(n²), он подходит только для небольших массивов. 17 | {{< /details >}} 18 | 19 | {{< details "Решение" close >}} 20 | 21 | ```java 22 | public class ArrayUtils { 23 | public static int[] sortArray(int[] arr) { 24 | for (int i = 0; i < arr.length - 1; i++) { 25 | for (int j = 0; j < arr.length - i - 1; j++) { 26 | if (arr[j] > arr[j + 1]) { 27 | int temp = arr[j]; 28 | arr[j] = arr[j + 1]; 29 | arr[j + 1] = temp; 30 | } 31 | } 32 | } 33 | return arr; 34 | } 35 | 36 | public static void main(String[] args) { 37 | int[] nums = {9, 4, 7, 3, 1}; 38 | int[] sorted = sortArray(nums); 39 | for (int n : sorted) { 40 | System.out.print(n + " "); 41 | } 42 | // Вывод: 1 3 4 7 9 43 | } 44 | } 45 | ``` 46 | 47 | 📌 **Что важно помнить:** 48 | ✅ Мы не используем `Arrays.sort()` — это ручная реализация. 49 | ✅ Это **пузырьковая сортировка**, простая в реализации. 50 | ✅ Подходит для обучения и простых задач, но неэффективна для больших массивов. 51 | ✅ Можно улучшить, добавив флаг для выхода при уже отсортированном массиве. 52 | 53 | {{< /details >}} 54 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-java-string-repetition.md: -------------------------------------------------------------------------------- 1 | #### 35. Проверка повторяющихся символов в строке 2 | 3 | **Условие задачи:** 4 | 🔥 Реализуйте метод, который принимает на вход непустую строку и проверяет, что в строке отсутствуют символы, повторяющиеся более двух раз. Метод возвращает **true**, если в строке нет символов с более чем двумя повторениями, и **false**, если такие символы присутствуют. 5 | 6 | 7 | {{< hint warning >}} **Спойлеры к решению** 8 | {{< /hint >}} 9 | 10 | {{< details "Подсказки" close >}} 11 | 💡 **Подсчет повторений:** Используйте `Map` для подсчета количества появлений каждого символа в строке. 12 | 💡 **Проверка лимита:** Если для любого символа количество появлений становится больше двух, сразу возвращайте `false`. 13 | 💡 **Обработка входных данных:** Добавьте проверку на пустую строку или `null` для повышения надежности. {{< /details >}} 14 | 15 | {{< details "Решение" close >}} 16 | 17 | ```java 18 | public class StringUtils { 19 | public static boolean validateString(String input) { 20 | if (input == null || input.isEmpty()) { 21 | throw new IllegalArgumentException("Строка не должна быть пустой"); 22 | } 23 | 24 | // Хранение количества повторений каждого символа 25 | Map charCount = new HashMap<>(); 26 | for (char c : input.toCharArray()) { 27 | int count = charCount.getOrDefault(c, 0) + 1; 28 | if (count > 2) { 29 | return false; 30 | } 31 | charCount.put(c, count); 32 | } 33 | return true; 34 | } 35 | 36 | public static void main(String[] args) { 37 | String validExample = "aabbcc"; // все символы повторяются не более двух раз 38 | String invalidExample = "aaabbc"; // символ 'a' повторяется 3 раза 39 | 40 | System.out.println("Пример 1: " + validExample + " -> " + validateString(validExample)); // true 41 | System.out.println("Пример 2: " + invalidExample + " -> " + validateString(invalidExample)); // false 42 | } 43 | } 44 | ``` 45 | 46 | 📌 **Что улучшилось?** 47 | ✅ **Эффективный подсчет:** Применение карты для быстрого подсчета повторений символов. 48 | ✅ **Ранняя остановка:** Метод возвращает `false`, сразу как только встречается символ, повторяющийся более двух раз. 49 | ✅ **Обработка ошибок:** Добавлена проверка на `null` и пустую строку для повышения надежности метода. {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-partition-list.md: -------------------------------------------------------------------------------- 1 | #### 11. Разделение списка на партиции 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | 🔹 Нам нужно разделить список на **подсписки фиксированного размера**. 10 | 🔹 Если длина списка **не делится нацело**, последний подсписок будет короче. 11 | 🔹 Можно использовать **подход с циклами** или **Stream API**. 12 | {{< /details >}} 13 | 14 | {{< details "Решение" close >}} 15 | **1️⃣ Способ: Обычный цикл** 16 | Простой и понятный способ с использованием `subList()`: 17 | 18 | ```java 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | class PartitionList { 23 | public static List> partition(List list, int size) { 24 | List> partitions = new ArrayList<>(); 25 | for (int i = 0; i < list.size(); i += size) { 26 | partitions.add(list.subList(i, Math.min(i + size, list.size()))); 27 | } 28 | return partitions; 29 | } 30 | 31 | public static void main(String[] args) { 32 | List numbers = new ArrayList<>(); 33 | for (int i = 1; i <= 10; i++) { 34 | numbers.add(i); 35 | } 36 | 37 | List> result = partition(numbers, 3); 38 | System.out.println(result); 39 | } 40 | } 41 | ``` 42 | 43 | ✅ **Вывод:** `[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]` 44 | 45 | **2️⃣ Способ: Stream API (Java 8+)** 46 | Используем `IntStream` для разделения списка: 47 | 48 | ```java 49 | import java.util.ArrayList; 50 | import java.util.List; 51 | import java.util.stream.Collectors; 52 | import java.util.stream.IntStream; 53 | 54 | class PartitionListStream { 55 | public static List> partition(List list, int size) { 56 | return IntStream.range(0, (int) Math.ceil((double) list.size() / size)) 57 | .mapToObj(i -> list.subList(i * size, Math.min((i + 1) * size, list.size()))) 58 | .collect(Collectors.toList()); 59 | } 60 | 61 | public static void main(String[] args) { 62 | List numbers = new ArrayList<>(); 63 | for (int i = 1; i <= 10; i++) { 64 | numbers.add(i); 65 | } 66 | 67 | List> result = partition(numbers, 3); 68 | System.out.println(result); 69 | } 70 | } 71 | ``` 72 | 73 | ✅ **Вывод:** `[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]` 74 | 75 | 💡 **Вывод:** Обычный цикл проще, но **Stream API** более декларативный 🚀 76 | {{< /details >}} 77 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-singleton-pattern.md: -------------------------------------------------------------------------------- 1 | #### 16. Написать Singleton 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | 🔹 Singleton — это паттерн, гарантирующий, что у класса есть только один экземпляр. 10 | 🔹 Лучший способ в Java — **"Lazy Initialization with Holder"**, так как он **потокобезопасный** и **ленивый**. 11 | 🔹 Можно использовать `enum`, но мы реализуем через класс. 12 | {{< /details >}} 13 | 14 | {{< details "Решение" close >}} 15 | 16 | ```java 17 | public class Singleton { 18 | private Singleton() { 19 | // Приватный конструктор, чтобы нельзя было создать через new 20 | } 21 | 22 | private static class SingletonHolder { 23 | private static final Singleton INSTANCE = new Singleton(); 24 | } 25 | 26 | public static Singleton getInstance() { 27 | return SingletonHolder.INSTANCE; 28 | } 29 | 30 | public void doSomething() { 31 | System.out.println("Работает Singleton!"); 32 | } 33 | 34 | public static void main(String[] args) { 35 | Singleton singleton1 = Singleton.getInstance(); 36 | Singleton singleton2 = Singleton.getInstance(); 37 | 38 | System.out.println(singleton1 == singleton2); // true 39 | singleton1.doSomething(); 40 | } 41 | } 42 | ``` 43 | 44 | 📌 **Объяснение:** 45 | 46 | - **Используется `private static class SingletonHolder`**, которая создается только при вызове `getInstance()`. 47 | - **Ленивая инициализация** — экземпляр создается только при первом вызове. 48 | - **Потокобезопасность** без `synchronized` за счет механизма `class loading` в Java. 49 | - **`singleton1 == singleton2` всегда `true`**, так как создается только один объект. 50 | 51 | 🔥 **Преимущества:** 52 | ✅ **Ленивая инициализация** — создается при первом вызове. 53 | ✅ **Без `synchronized`** — нет накладных расходов. 54 | ✅ **Потокобезопасность** благодаря механизму загрузки классов. 🚀 55 | {{< /details >}} 56 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-spring-auto-robot.md: -------------------------------------------------------------------------------- 1 | #### 17. Спринг сервис. Идеи по улучшению кода. Как сделать так, чтобы при добавлении нового робота Spring сразу создавал нового робота и нам не пришлось работать ручками 2 | 3 | ```java 4 | @Component 5 | public class Robot1 { 6 | public void startWork() { 7 | // помыть полы 8 | } 9 | } 10 | 11 | @Component 12 | public class Robot2 { 13 | public void startWork() { 14 | // почистить стены 15 | } 16 | } 17 | 18 | @Component 19 | public class Robot3 { 20 | public void startWork() { 21 | // играет в футбол 22 | } 23 | } 24 | 25 | @Service 26 | public class RobotProcessing { 27 | 28 | @Autowired 29 | private Robot1 r1; 30 | 31 | @Autowired 32 | private Robot2 r2; 33 | 34 | @Autowired 35 | private Robot3 r3; 36 | 37 | public void startAllRobots() { 38 | r1.startWork(); 39 | r2.startWork(); 40 | r3.startWork(); 41 | } 42 | } 43 | 44 | ``` 45 | 46 | 47 | {{< hint warning >}} 48 | **Спойлеры к решению**   49 | {{< /hint >}} 50 | 51 | {{< details "Подсказки" close >}} 52 | 🤖 Проблема в том, что **при добавлении нового робота нужно вручную менять код в `RobotProcessing`**. 53 | ⚙️ Можно использовать **список `List` или `Map`**, чтобы Spring автоматически собирал всех бинов-роботов. 54 | 📌 **Интерфейс** поможет сделать код гибким и расширяемым. 55 | {{< /details >}} 56 | 57 | {{< details "Решение" close >}} 58 | 59 | ```java 60 | public interface Robot { 61 | void startWork(); 62 | } 63 | 64 | @Component 65 | public class Robot1 implements Robot { 66 | @Override 67 | public void startWork() { 68 | System.out.println("Robot1 моет полы"); 69 | } 70 | } 71 | 72 | @Component 73 | public class Robot2 implements Robot { 74 | @Override 75 | public void startWork() { 76 | System.out.println("Robot2 чистит стены"); 77 | } 78 | } 79 | 80 | @Component 81 | public class Robot3 implements Robot { 82 | @Override 83 | public void startWork() { 84 | System.out.println("Robot3 играет в футбол"); 85 | } 86 | } 87 | 88 | @Service 89 | public class RobotProcessing { 90 | 91 | private final List robots; 92 | 93 | @Autowired 94 | public RobotProcessing(List robots) { 95 | this.robots = robots; 96 | } 97 | 98 | public void startAllRobots() { 99 | robots.forEach(Robot::startWork); 100 | } 101 | } 102 | ``` 103 | 104 | 📌 **Объяснение:** 105 | 106 | - **Добавили интерфейс `Robot`**, чтобы все роботы его реализовывали. 107 | - **Используем `List` в `RobotProcessing`**, и Spring автоматически находит все реализации `Robot`. 108 | - **Теперь можно просто создать новый класс с `@Component`, и Spring сам его добавит в список!** 109 | 110 | 🔥 **Преимущества:** 111 | ✅ **Гибкость** — не нужно изменять `RobotProcessing` при добавлении нового робота. 112 | ✅ **Инкапсуляция** — `RobotProcessing` не зависит от конкретных классов роботов. 113 | ✅ **Меньше кода и ошибок** 🚀 114 | {{< /details >}} 115 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-authors-books.md: -------------------------------------------------------------------------------- 1 | #### 26. Таблицы для авторов и книг (многие-ко-многим) 2 | 3 | **Условие задачи:** 4 | 📌 Есть две сущности: **Авторы** и **Книги**. 5 | Каждый автор может иметь много книг, и каждая книга может иметь много авторов (отношение «многие-ко-многим»). 6 | Необходимо описать структуру таблиц для реализации такой связи. 7 | 8 | {{< hint warning >}} 9 | **Спойлеры к решению** 10 | {{< /hint >}} 11 | 12 | {{< details "Подсказки" close >}} 13 | 💡 В классической реляционной модели многие-ко-многим реализуется через **промежуточную (junction) таблицу**. 14 | 💡 Основные таблицы — `authors` и `books` — содержат информацию об авторах и книгах соответственно. 15 | 💡 Junction-таблица хранит пары `(author_id, book_id)`. 16 | {{< /details >}} 17 | 18 | {{< details "Решение" close >}} 19 | ```sql 20 | -- Основная таблица авторов 21 | CREATE TABLE authors ( 22 | id SERIAL PRIMARY KEY, 23 | name VARCHAR(255) NOT NULL 24 | ); 25 | 26 | -- Основная таблица книг 27 | CREATE TABLE books ( 28 | id SERIAL PRIMARY KEY, 29 | title VARCHAR(255) NOT NULL 30 | ); 31 | 32 | -- Промежуточная таблица для связи многие-ко-многим 33 | CREATE TABLE author_book ( 34 | author_id INT NOT NULL, 35 | book_id INT NOT NULL, 36 | PRIMARY KEY (author_id, book_id), 37 | FOREIGN KEY (author_id) REFERENCES authors(id), 38 | FOREIGN KEY (book_id) REFERENCES books(id) 39 | ); 40 | ``` 41 | 42 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-big-spenders.md: -------------------------------------------------------------------------------- 1 | #### 8. Получить id пользователей, потративших более 1 000 000 рублей (без подзапросов)? 2 | 3 | ``` 4 | Представим, что у нас есть свой магазин. К нам приходят люди и покупают у нас что-то. Мы храним всю историю покупок всех пользователей в таблице в базе данных. Таблица выглядит следующим образом: 5 | orders: 6 | id - автоинкрементный первичный ключ, 7 | user_id - идентификатор пользователя 8 | amount - сумма покупки которую совершил пользователь 9 | 10 | Мы оперируем только этой одной таблицей и считаем, что других таблиц нет. Требуется написать SQL запрос для получения идентификаторов всех пользователей, которые совершили покупки за все время на сумму больше чем на 1 000 000 рублей. 11 | Ограничения: Запрос надо написать без использования подзапросов. 12 | 13 | ``` 14 | 15 | {{< hint warning >}} 16 | **Спойлеры к решению**   17 | {{< /hint >}} 18 | 19 | {{< details "Подсказки" close >}} 20 | 📊 Нужно найти **пользователей**, чья общая сумма покупок **больше 1 000 000**. 21 | 🔗 Таблица `orders` содержит **user_id** и **amount** (сумма покупки). 22 | 🧮 Нам нужно **суммировать** `amount` по каждому `user_id`. 23 | 🚀 Используем **`GROUP BY`** для группировки и **`HAVING`** для фильтрации. 24 | {{< /details >}} 25 | 26 | {{< details "Решение" close >}} 27 | 28 | ```sql 29 | SELECT user_id 30 | FROM orders 31 | GROUP BY user_id 32 | HAVING SUM(amount) > 1000000; 33 | ``` 34 | 35 | 📌 **Объяснение:** 36 | 37 | - **`GROUP BY user_id`** – группируем заказы по `user_id`. 38 | - **`SUM(amount)`** – считаем общую сумму покупок каждого пользователя. 39 | - **`HAVING SUM(amount) > 1000000`** – отбираем только тех, кто **потратил больше миллиона**. 40 | 41 | ✅ **Готово!** 🛍🔥 42 | {{< /details >}} 43 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-company-department-employee-count.md: -------------------------------------------------------------------------------- 1 | #### 12. Покажите название компании, название отдела и количество сотрудников в отделе 2 | 3 | **Условие задачи:** 4 | 🔍 **Написать SQL-запрос, который покажет название компании, название отдела и количество сотрудников в этом отделе.** 5 | 6 | ```sql 7 | CREATE table if not exists company ( 8 | id uuid primary key, 9 | name_ varchar NOT NULL 10 | ); 11 | 12 | CREATE table if not exists department ( 13 | id uuid primary key, 14 | name_ varchar NOT NULL, 15 | company_id uuid NOT NULL, 16 | CONSTRAINT department_fk FOREIGN KEY (company_id) REFERENCES company(id) 17 | ); 18 | 19 | CREATE table if not exists employee ( 20 | id uuid primary key, 21 | name_ varchar NOT NULL, 22 | department_id uuid NOT NULL, 23 | CONSTRAINT employee_fk FOREIGN KEY (department_id) REFERENCES department(id) 24 | ); 25 | 26 | insert into company (id, name_) values(1, 'Company 1') ON CONFLICT DO NOTHING; 27 | insert into company (id, name_) values(2, 'Company 2') ON CONFLICT DO NOTHING; 28 | insert into company (id, name_) values(3, 'Company 3') ON CONFLICT DO NOTHING; 29 | 30 | insert into department (id, name_, company_id) values(1, 'Department 1', 1) ON CONFLICT DO NOTHING; 31 | insert into department (id, name_, company_id) values(2, 'Department 2', 1) ON CONFLICT DO NOTHING; 32 | insert into department (id, name_, company_id) values(3, 'Department 3', 2) ON CONFLICT DO NOTHING; 33 | 34 | insert into employee (id, name_, department_id) values(1, 'Employee 1', 1) ON CONFLICT DO NOTHING; 35 | insert into employee (id, name_, department_id) values(2, 'Employee 2', 2) ON CONFLICT DO NOTHING; 36 | insert into employee (id, name_, department_id) values(3, 'Employee 3', 3) ON CONFLICT DO NOTHING; 37 | 38 | ``` 39 | 40 | 41 | {{< hint warning >}} 42 | **Спойлеры к решению**   43 | {{< /hint >}} 44 | 45 | {{< details "Подсказки" close >}} 46 | 💡 Нужно использовать `JOIN` для соединения таблиц `company`, `department` и `employee`. 47 | 💡 Используйте `COUNT(*)` для подсчета количества сотрудников в каждом отделе. 48 | 💡 Группировка `GROUP BY` поможет сгруппировать результаты по компании и отделу. 49 | {{< /details >}} 50 | 51 | {{< details "Решение" close >}} 52 | 53 | ```sql 54 | SELECT 55 | c.name_ AS company_name, 56 | d.name_ AS department_name, 57 | COUNT(e.id) AS employee_count 58 | FROM company c 59 | JOIN department d ON c.id = d.company_id 60 | LEFT JOIN employee e ON d.id = e.department_id 61 | GROUP BY c.name_, d.name_ 62 | ORDER BY c.name_, d.name_; 63 | ``` 64 | 65 | ✅ **Объяснение решения:** 66 | 67 | - Соединяем `company` и `department` по `company_id`. 68 | - Используем `LEFT JOIN`, чтобы учесть отделы без сотрудников. 69 | - `COUNT(e.id)` считает количество сотрудников в каждом отделе. 70 | - `GROUP BY` группирует данные по названию компании и отдела. 71 | - `ORDER BY` сортирует результат по компании и отделу. 72 | 73 | 🚀 **Теперь запрос выдаст список всех компаний, их отделов и количество сотрудников в каждом отделе!** 🔥 74 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-customers-multiple-orders.md: -------------------------------------------------------------------------------- 1 | #### 10. Выбрать всех клиентов, у которых больше трех заказов с ценой > 10 2 | 3 | ``` 4 | Customer 5 | - Id 6 | - name 7 | 8 | Order 9 | - Id 10 | - customer_id 11 | - Price 12 | 13 | ``` 14 | 15 | 16 | 17 | {{< hint warning >}} 18 | **Спойлеры к решению**   19 | {{< /hint >}} 20 | 21 | {{< details "Подсказки" close >}} 22 | 🔍 Мы должны выбрать всех клиентов, которые сделали **более трех заказов**, где **цена** каждого заказа больше **10**. 23 | 📊 Для этого нужно группировать заказы по **`customer_id`** и подсчитывать количество заказов с ценой выше 10. 24 | 📌 Используем **`HAVING`** для фильтрации по количеству заказов. 25 | {{< /details >}} 26 | 27 | {{< details "Решение" close >}} 28 | 29 | ```sql 30 | SELECT o.customer_id, c.name 31 | FROM Order o 32 | JOIN Customer c ON o.customer_id = c.Id 33 | WHERE o.Price > 10 34 | GROUP BY o.customer_id, c.name 35 | HAVING COUNT(o.Id) > 3; 36 | ``` 37 | 38 | 📌 **Объяснение:** 39 | 40 | - **`JOIN`** соединяет таблицы **`Order`** и **`Customer`** по полю **`customer_id`**. 41 | - **`WHERE o.Price > 10`** фильтрует только те заказы, где цена больше 10. 42 | - **`GROUP BY o.customer_id, c.name`** группирует по **`customer_id`** и имени клиента. 43 | - **`HAVING COUNT(o.Id) > 3`** выбирает только тех клиентов, у которых больше 3 заказов с ценой больше 10. 44 | 45 | ✅ **Задача решена!** 🎯 46 | {{< /details >}} 47 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-departments-and-high-salary-employees.md: -------------------------------------------------------------------------------- 1 | #### 33. Отделы с числом сотрудников и сотрудники с окладом выше руководителя 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблицы 5 | 6 | ```sql 7 | -- Модель данных 8 | -- Отдел: 9 | CREATE TABLE department ( 10 | id INTEGER NOT NULL, -- идентификатор отдела 11 | name VARCHAR(128) NOT NULL, -- название отдела 12 | PRIMARY KEY (id) 13 | ); 14 | 15 | -- Сотрудник: 16 | CREATE TABLE employee ( 17 | id INTEGER NOT NULL, -- идентификатор сотрудника 18 | department_id INTEGER NOT NULL, -- идентификатор отдела 19 | manager_id INTEGER, -- идентификатор начальника 20 | name VARCHAR(128) NOT NULL, -- имя сотрудника 21 | salary DECIMAL NOT NULL, -- оклад сотрудника 22 | PRIMARY KEY (id), 23 | FOREIGN KEY (department_id) REFERENCES department(id), 24 | FOREIGN KEY (manager_id) REFERENCES employee(id) 25 | ); 26 | 27 | 28 | ``` 29 | 30 | 1. Вывести список отделов в алфавитном порядке с количеством их сотрудников. 31 | 2. Вывести список сотрудников, чей оклад больше, чем у их непосредственного руководителя. 32 | 33 | {{< hint warning >}} 34 | **Спойлеры к решению** 35 | {{< /hint >}} 36 | 37 | {{< details "Подсказки" close >}} 38 | 💡 Для первой задачи используйте `LEFT JOIN` или `JOIN` между `department` и `employee`, агрегируйте по `department.name`, `COUNT(employee.id)` и сортируйте по имени отдела. 39 | 💡 Для второй задачи выполните самосоединение `employee` как `e` и `manager`, сопоставляя `e.manager_id = manager.id`, и отфильтруйте по условию `e.salary > manager.salary`. 40 | {{< /details >}} 41 | 42 | {{< details "Решение" close >}} 43 | ```sql 44 | -- 1. Список отделов и количество сотрудников 45 | SELECT 46 | d.name AS department_name, 47 | COUNT(e.id) AS employee_count 48 | FROM department d 49 | LEFT JOIN employee e 50 | ON e.department_id = d.id 51 | GROUP BY d.name 52 | ORDER BY d.name; 53 | 54 | -- 2. Сотрудники с окладом выше, чем у непосредственного руководителя 55 | SELECT 56 | e.name AS employee_name, 57 | e.salary AS employee_salary, 58 | m.name AS manager_name, 59 | m.salary AS manager_salary 60 | FROM employee e 61 | JOIN employee m 62 | ON e.manager_id = m.id 63 | WHERE e.salary > m.salary; 64 | ``` 65 | 66 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-duplicate-names.md: -------------------------------------------------------------------------------- 1 | #### 9. БД таблица users, два столбца: id number autoincrement unique, name varchar not unique. Написать запрос, чтобы вывелись имена, которые встречаются 2 и более раз 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | 🔎 Нужно найти имена, которые встречаются **2 и более раз** в таблице `users`. 10 | 📊 Мы будем группировать по полю `name` и фильтровать по количеству таких значений. 11 | 🚀 Для подсчета количества будем использовать **`COUNT(name)`**. 12 | 📌 Для фильтрации количества используем **`HAVING`**. 13 | {{< /details >}} 14 | 15 | {{< details "Решение" close >}} 16 | 17 | ```sql 18 | SELECT name 19 | FROM users 20 | GROUP BY name 21 | HAVING COUNT(name) >= 2; 22 | ``` 23 | 24 | 📌 **Объяснение:** 25 | 26 | - **`GROUP BY name`** – группируем по имени, чтобы подсчитать количество одинаковых значений. 27 | - **`COUNT(name)`** – считаем количество повторений каждого имени. 28 | - **`HAVING COUNT(name) >= 2`** – выбираем только те имена, которые встречаются **2 и более раз**. 29 | 30 | ✅ **Готово!** 🎉 31 | {{< /details >}} 32 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-empty-classes.md: -------------------------------------------------------------------------------- 1 | #### 7. Вывести id классов, у которых нет ни одного студента (без подзапросов)? 2 | 3 | ``` 4 | "Есть 2 таблицы student и class 5 | student: 6 | id - автоинкрементный первичный ключ, 7 | class_id - id класса в котором числится студент 8 | class: 9 | id - автоинкрементный первичный ключ 10 | Нужно написать запрос для вывода id классов, 11 | у которых нет ни одного студента еще. 12 | Ограничения: Запрос надо написать без использования подзапросов." 13 | 14 | ``` 15 | 16 | 17 | {{< hint warning >}} 18 | **Спойлеры к решению**   19 | {{< /hint >}} 20 | 21 | {{< details "Подсказки" close >}} 22 | 🎯 Нужно выбрать классы, в которых **нет студентов**. 23 | 🔗 Таблицы **`class`** и **`student`** связаны через `class_id`. 24 | 🛑 Важно **отфильтровать** те классы, у которых `student.id IS NULL`. 25 | 📊 Используем **`LEFT JOIN`**, чтобы увидеть классы **без соответствий** в `student`. 26 | {{< /details >}} 27 | 28 | {{< details "Решение" close >}} 29 | 30 | ```sql 31 | SELECT c.id 32 | FROM class c 33 | LEFT JOIN student s ON c.id = s.class_id 34 | WHERE s.id IS NULL; 35 | ``` 36 | 37 | 📌 **Объяснение:** 38 | 39 | - **`LEFT JOIN`** – соединяет все классы и подставляет `NULL` в `s.id`, если студентов нет. 40 | - **`WHERE s.id IS NULL`** – выбираем только те классы, в которых студентов **нет**. 41 | 42 | ✅ **Готово!** 🚀 43 | {{< /details >}} 44 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-export-news-with-comments.md: -------------------------------------------------------------------------------- 1 | #### 22. Выгрузка всех новостей с комментариями при большом объеме данных 2 | 3 | 4 | **Условие задачи:** 5 | 📌 **Необходимо получить все записи из таблицы `news` и соответствующие комментарии из таблицы `comments`, даже при большом объеме данных.** 6 | 7 | - Таблица `news` содержит новости (`id` — автоинкремент). 8 | - Таблица `comments` содержит комментарии (`news_id` ссылается на `news.id`). 9 | - Допустим, данных **очень много**, важно избежать перегрузки. 10 | 11 | {{< hint warning >}} 12 | **Спойлеры к решению** 13 | {{< /hint >}} 14 | 15 | {{< details "Подсказки" close >}} 16 | 💡 Используй **постраничную выборку (pagination)**, чтобы обрабатывать данные частями. 17 | 💡 Важно делать **LEFT JOIN**, чтобы не потерять новости без комментариев. 18 | 💡 Можно использовать **потоковую обработку** или **batch-загрузку** на стороне кода. 19 | 💡 В PostgreSQL можно использовать `FETCH NEXT`, в MySQL — `LIMIT` и `OFFSET`. 20 | {{< /details >}} 21 | 22 | {{< details "Решение" close >}} 23 | 24 | ```sql 25 | -- Пример базового запроса с LEFT JOIN 26 | SELECT n.*, c.* 27 | FROM news n 28 | LEFT JOIN comments c ON n.id = c.news_id; 29 | ```` 30 | 31 | ✅ **Как сделать выгрузку эффективной при большом объеме данных:** 32 | 33 | 1. **Использовать постраничную загрузку:** 34 | 35 | 36 | ```sql 37 | -- Пример с LIMIT и OFFSET 38 | SELECT n.*, c.* 39 | FROM news n 40 | LEFT JOIN comments c ON n.id = c.news_id 41 | ORDER BY n.id 42 | LIMIT 1000 OFFSET 0; -- на следующей итерации OFFSET будет 1000, затем 2000 и т.д. 43 | ``` 44 | 45 | 2. **Использовать потоковую обработку на сервере (например, JDBC или ORM):** 46 | 47 | 48 | ```java 49 | PreparedStatement stmt = connection.prepareStatement( 50 | "SELECT n.*, c.* FROM news n LEFT JOIN comments c ON n.id = c.news_id ORDER BY n.id", 51 | ResultSet.TYPE_FORWARD_ONLY, 52 | ResultSet.CONCUR_READ_ONLY 53 | ); 54 | stmt.setFetchSize(1000); 55 | ResultSet rs = stmt.executeQuery(); 56 | ``` 57 | 58 | 3. **Использовать выгрузку по диапазону `id` (например, `WHERE n.id BETWEEN ...`), чтобы избежать OFFSET.** 59 | 60 | 61 | ```sql 62 | SELECT n.*, c.* 63 | FROM news n 64 | LEFT JOIN comments c ON n.id = c.news_id 65 | WHERE n.id BETWEEN 10001 AND 11000 66 | ORDER BY n.id; 67 | ``` 68 | 69 | 🔥 Таким образом, даже при большом количестве строк можно **гибко управлять производительностью и ресурсами**, не перегружая систему. {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-find-clients-by-inn-kpp.md: -------------------------------------------------------------------------------- 1 | #### 17. Поиск клиентов в базе данных по INN и KPP 2 | 3 | 4 | **Условие задачи:** 5 | 📌 **Написать SQL-запрос для поиска всех клиентов по `inn` и `kpp` в таблице `KF_ADAPTER_R_CLIENT`.** 6 | 7 | - Используется **простая выборка** по одной таблице. 8 | - В таблице `KF_ADAPTER_R_CLIENT` есть информация о клиентах, включая `inn` (ИНН) и `kpp` (КПП). 9 | 10 | 11 | ```sql 12 | CREATE TABLE KF_ADAPTER_R_CLIENT ( 13 | id SERIAL PRIMARY KEY, 14 | abs_id VARCHAR(255), 15 | branch_id VARCHAR(255), 16 | abs_branch_id VARCHAR(255), 17 | short_name VARCHAR(255), 18 | inn VARCHAR(12), 19 | kpp VARCHAR(9), 20 | sign_count INT, 21 | deleted BOOLEAN 22 | ); 23 | 24 | CREATE TABLE KF_ADAPTER_R_DELEGATE ( 25 | id SERIAL PRIMARY KEY, 26 | client_id INT, 27 | abs_id VARCHAR(255), 28 | user_id VARCHAR(255), 29 | full_name VARCHAR(255), 30 | phone VARCHAR(15), 31 | sign_role VARCHAR(50), 32 | deleted BOOLEAN, 33 | FOREIGN KEY (client_id) REFERENCES KF_ADAPTER_R_CLIENT(id) 34 | ); 35 | 36 | ``` 37 | 38 | 39 | {{< hint warning >}} 40 | **Спойлеры к решению**   41 | {{< /hint >}} 42 | 43 | {{< details "Подсказки" close >}} 44 | 💡 Для поиска клиентов используем `WHERE`. 45 | 💡 `inn` и `kpp` могут иметь разные значения, поэтому проверяем их **совместно**. 46 | 💡 Если нужно искать клиентов **с определенными значениями**, используем `=`. 47 | 💡 Если необходимо найти клиентов с **похожими значениями**, используем `LIKE`. 48 | {{< /details >}} 49 | 50 | {{< details "Решение" close >}} 51 | 52 | ```sql 53 | SELECT * 54 | FROM KF_ADAPTER_R_CLIENT 55 | WHERE inn = '7701234567' 56 | AND kpp = '770101001'; 57 | ``` 58 | 59 | ✅ **Объяснение решения:** 60 | 61 | - Запрос выбирает **все колонки** (`*`) из таблицы `KF_ADAPTER_R_CLIENT`. 62 | - Фильтрация выполняется по точному совпадению **ИНН** и **КПП** (`WHERE inn = ... AND kpp = ...`). 63 | - Если **нужен поиск по частичному совпадению**, можно использовать `LIKE`: 64 | 65 | ```sql 66 | SELECT * 67 | FROM KF_ADAPTER_R_CLIENT 68 | WHERE inn LIKE '770%' 69 | AND kpp LIKE '7701%'; 70 | ``` 71 | 72 | 🔥 Теперь можно находить клиентов по `inn` и `kpp` быстро и просто! 🚀 73 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-find-delegates-by-inn-kpp.md: -------------------------------------------------------------------------------- 1 | #### 18. Делегаты клиентов: найти по INN/KPP, вывести имя и телефон 2 | 3 | **Условие задачи:** 4 | 📌 **Написать SQL-запрос для получения списка делегатов (`KF_ADAPTER_R_DELEGATE`) по `inn` и `kpp` из таблицы `KF_ADAPTER_R_CLIENT`.** 5 | 6 | - Необходимо вывести только `full_name` (полное имя) и `phone` (телефон). 7 | - Таблицы связаны по `client_id`. 8 | 9 | 10 | ```sql 11 | CREATE TABLE KF_ADAPTER_R_CLIENT ( 12 | id SERIAL PRIMARY KEY, 13 | abs_id VARCHAR(255), 14 | branch_id VARCHAR(255), 15 | abs_branch_id VARCHAR(255), 16 | short_name VARCHAR(255), 17 | inn VARCHAR(12), 18 | kpp VARCHAR(9), 19 | sign_count INT, 20 | deleted BOOLEAN 21 | ); 22 | 23 | CREATE TABLE KF_ADAPTER_R_DELEGATE ( 24 | id SERIAL PRIMARY KEY, 25 | client_id INT, 26 | abs_id VARCHAR(255), 27 | user_id VARCHAR(255), 28 | full_name VARCHAR(255), 29 | phone VARCHAR(15), 30 | sign_role VARCHAR(50), 31 | deleted BOOLEAN, 32 | FOREIGN KEY (client_id) REFERENCES KF_ADAPTER_R_CLIENT(id) 33 | ); 34 | 35 | ``` 36 | 37 | 38 | {{< hint warning >}} 39 | **Спойлеры к решению**   40 | {{< /hint >}} 41 | 42 | {{< details "Подсказки" close >}} 43 | 💡 Связь между таблицами идет по **client_id**. 44 | 💡 Для соединения таблиц используем `JOIN`. 45 | 💡 Фильтруем данные по `inn` и `kpp` из таблицы `KF_ADAPTER_R_CLIENT`. 46 | 💡 Если необходимо исключить удаленных (`deleted = TRUE`), добавляем соответствующий фильтр. 47 | 48 | {{< /details >}} 49 | 50 | {{< details "Решение" close >}} 51 | 52 | ```sql 53 | SELECT d.full_name, d.phone 54 | FROM KF_ADAPTER_R_DELEGATE d 55 | JOIN KF_ADAPTER_R_CLIENT c ON d.client_id = c.id 56 | WHERE c.inn = '7701234567' 57 | AND c.kpp = '770101001' 58 | AND d.deleted = FALSE; 59 | ``` 60 | 61 | ✅ **Объяснение решения:** 62 | 63 | - **`JOIN`** связывает таблицы `KF_ADAPTER_R_CLIENT` и `KF_ADAPTER_R_DELEGATE` по `client_id`. 64 | - **Фильтр по `inn` и `kpp`** выбирает только клиентов с указанными значениями. 65 | - **`d.deleted = FALSE`** исключает удаленных делегатов. 66 | - **Выбираем только нужные поля**: `full_name` и `phone`. 67 | 68 | 🔥 Теперь можно получать список делегатов по ИНН и КПП! 🚀 69 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-fish-with-catch-less-than.md: -------------------------------------------------------------------------------- 1 | #### 27. Рыбы с суммарным уловом на дату d менее n 2 | 3 | 4 | **Условие задачи:** 5 | 📌 Есть таблицы: 6 | - **fish** (`id`, `name`) 7 | - **catch** (`id`, `fish_id`, `dt`, `quantity`) 8 | 9 | Необходимо вывести **наименования рыб**, для которых **суммарный улов** на указанную дату `d` **меньше** чем `n`. 10 | 11 | {{< hint warning >}} 12 | **Спойлеры к решению** 13 | {{< /hint >}} 14 | 15 | {{< details "Подсказки" close >}} 16 | 💡 Фильтр по дате делается в `WHERE dt = :d`. 17 | 💡 Суммарный улов считаем через `SUM(quantity)` и группировку по `fish_id`. 18 | 💡 Используйте `HAVING SUM(quantity) < :n` для первой реализации. 19 | 💡 Во втором варианте можно применить **подзапрос** либо **LEFT JOIN** и фильтрацию по агрегату. 20 | {{< /details >}} 21 | 22 | {{< details "Решение" close >}} 23 | -- Решение 1: GROUP BY + HAVING 24 | ```sql 25 | SELECT f.name 26 | FROM fish f 27 | JOIN catch c ON f.id = c.fish_id 28 | WHERE c.dt = :d 29 | GROUP BY f.name 30 | HAVING SUM(c.quantity) < :n; 31 | ``` 32 | 33 | -- Решение 2: Подзапрос в WHERE 34 | 35 | ```sql 36 | SELECT f.name 37 | FROM fish f 38 | WHERE ( 39 | SELECT COALESCE(SUM(c.quantity), 0) 40 | FROM catch c 41 | WHERE c.fish_id = f.id 42 | AND c.dt = :d 43 | ) < :n; 44 | ``` 45 | 46 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-folders-with-avi-or-empty.md: -------------------------------------------------------------------------------- 1 | #### 37. Папки с AVI-файлами и пустые папки 2 | 3 | 4 | **Условие задачи:** 5 | 📌 Написать SQL-запрос, который выбрал бы **названия папок**, в которых есть хотя бы один файл с расширением `.avi`, **или** которые являются **пустыми** (не содержат ни одного файла). 6 | 7 | **Структура таблиц:** 8 | 9 | ```sql 10 | CREATE TABLE folder ( 11 | id uuid PRIMARY KEY, 12 | name text NOT NULL 13 | ); 14 | 15 | CREATE TABLE file ( 16 | id uuid PRIMARY KEY, 17 | name text NOT NULL, 18 | folder_id uuid NOT NULL, 19 | CONSTRAINT fk__file__folder_id FOREIGN KEY (folder_id) REFERENCES folder (id) 20 | ); 21 | 22 | ``` 23 | 24 | {{< hint warning >}} 25 | **Спойлеры к решению** 26 | {{< /hint >}} 27 | 28 | {{< details "Подсказки" close >}} 29 | 💡 Используем `LEFT JOIN` — чтобы получить и те папки, в которых **нет файлов вообще**. 30 | 💡 Для файлов с `.avi` используем фильтр `file.name LIKE '%.avi'`. 31 | 💡 Для определения пустых папок проверяем `file.id IS NULL`. 32 | 💡 Не забудьте использовать `DISTINCT`, если JOIN может дать дубликаты. 33 | {{< /details >}} 34 | 35 | {{< details "Решение" close >}} 36 | ```sql 37 | SELECT DISTINCT f.name 38 | FROM folder f 39 | LEFT JOIN file fi ON f.id = fi.folder_id 40 | WHERE fi.name LIKE '%.avi' 41 | OR fi.id IS NULL; 42 | ``` 43 | 44 | ✅ **Объяснение решения:** 45 | 46 | - `LEFT JOIN` соединяет папки с файлами (если они есть), оставляя строки для пустых папок. 47 | 48 | - `fi.name LIKE '%.avi'` — выбираем папки с AVI-файлами. 49 | 50 | - `fi.id IS NULL` — означает, что в папке нет ни одного файла. 51 | 52 | - `DISTINCT` убирает возможные дубликаты, если в папке несколько AVI-файлов. 53 | 54 | 55 | 🔥 Один запрос — и у нас и видео, и пустота 😎 56 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-groups-without-services.md: -------------------------------------------------------------------------------- 1 | #### 25. Группы без услуг 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблицы: 5 | - **Services** (`id`, `name`, `cost`, `price`, `group_id`) 6 | - **Groups** (`id`, `name`) 7 | 8 | Необходимо вывести **названия групп**, в которых **нет ни одной услуги**. 9 | 10 | {{< hint warning >}} 11 | **Спойлеры к решению** 12 | {{< /hint >}} 13 | 14 | {{< details "Подсказки" close >}} 15 | 💡 Используйте **LEFT JOIN** между `Groups` и `Services`, а в `WHERE` проверяйте `NULL` в полях `Services`. 16 | 💡 Альтернативно, примените **NOT EXISTS** с подзапросом, проверяющим отсутствие услуг в группе. 17 | {{< /details >}} 18 | 19 | {{< details "Решение" close >}} 20 | ```sql 21 | -- Решение 1: LEFT JOIN + фильтрация по NULL 22 | SELECT g.name 23 | FROM Groups g 24 | LEFT JOIN Services s ON g.id = s.group_id 25 | WHERE s.id IS NULL; 26 | 27 | -- Решение 2: NOT EXISTS 28 | SELECT g.name 29 | FROM Groups g 30 | WHERE NOT EXISTS ( 31 | SELECT 1 32 | FROM Services s 33 | WHERE s.group_id = g.id 34 | ); 35 | ``` 36 | 37 | {{< /details >}} 38 | 39 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-match-strings-with-masks.md: -------------------------------------------------------------------------------- 1 | #### 34. Строки, соответствующие любому шаблону 2 | 3 | **Условие задачи:** 4 | 📌 Есть две таблицы: 5 | - `t1(str)` – содержит строки 6 | - `t2(mask)` – содержит шаблоны для `LIKE` 7 | 8 | Необходимо выбрать все строки из `t1`, которые соответствуют хотя бы одному шаблону из `t2` с помощью `SQL LIKE`. 9 | 10 | {{< hint warning >}} 11 | **Спойлеры к решению** 12 | {{< /hint >}} 13 | 14 | {{< details "Подсказки" close >}} 15 | 💡 Используйте `JOIN` по условию `t1.str LIKE t2.mask`, чтобы отфильтровать подходящие строки. 16 | 💡 Можно удалить дубликаты с помощью `DISTINCT`, если одна строка подходит под несколько шаблонов. 17 | 💡 Вариант с `EXISTS` помогает избежать дублирования и читабелен для проверки "хотя бы одного" шаблона. 18 | {{< /details >}} 19 | 20 | {{< details "Решение" close >}} 21 | ```sql 22 | -- Решение 1: JOIN + DISTINCT 23 | SELECT DISTINCT t1.str 24 | FROM t1 25 | JOIN t2 26 | ON t1.str LIKE t2.mask; 27 | 28 | -- Решение 2: EXISTS 29 | SELECT t1.str 30 | FROM t1 31 | WHERE EXISTS ( 32 | SELECT 1 33 | FROM t2 34 | WHERE t1.str LIKE t2.mask 35 | ); 36 | ``` 37 | 38 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-max-price-by-category.md: -------------------------------------------------------------------------------- 1 | #### 16. Товары с максимальной ценой в каждой категории товаров 2 | 3 | 4 | **Условие задачи:** 5 | 📊 **Составить SQL-запрос, который выводит наименования товаров с максимальной ценой в каждой категории.** 6 | 7 | 📌 **Таблица `Prices` (данные о товарах):** 8 | 9 | - `Name` – название товара 10 | - `Category` – категория товара 11 | - `Price` – цена товара 12 | 13 | 🚀 **Требуется вернуть:** 14 | 15 | - `Category` 16 | - `Name` (товар с максимальной ценой в категории) 17 | - `Price` (максимальная цена в категории) 18 | 19 | 20 | ```text 21 | Дана таблица Prices в БД. 22 | Name Category Price 23 | -------------------------------------- 24 | Апельсин Фрукты 50 25 | Мандарин Фрукты 45 26 | Яблоко Фрукты 30 27 | Огурец Овощи 35 28 | Томат Овощи 40 29 | Картофель Овощи 25 30 | Молоко Молочные продукты 30 31 | Творог Молочные продукты 40 32 | 33 | 34 | ``` 35 | 36 | 37 | {{< hint warning >}} 38 | **Спойлеры к решению**   39 | {{< /hint >}} 40 | 41 | {{< details "Подсказки" close >}} 42 | 💡 Для каждой категории находим **максимальную цену** с помощью `MAX(Price)`. 43 | 💡 Затем выбираем товары, у которых `Price` равно найденному максимуму. 44 | 💡 Используем `JOIN` или `WHERE` с подзапросом. 45 | {{< /details >}} 46 | 47 | {{< details "Решение" close >}} 48 | 49 | ```sql 50 | SELECT p1.Category, p1.Name, p1.Price 51 | FROM Prices p1 52 | WHERE p1.Price = ( 53 | SELECT MAX(p2.Price) 54 | FROM Prices p2 55 | WHERE p1.Category = p2.Category 56 | ); 57 | ``` 58 | 59 | ✅ **Объяснение решения:** 60 | 61 | - Подзапрос `SELECT MAX(p2.Price) FROM Prices p2 WHERE p1.Category = p2.Category` находит **максимальную цену в каждой категории**. 62 | - Основной запрос выбирает **товары с этой максимальной ценой**. 63 | - Если в категории несколько товаров с одинаковой максимальной ценой, они **все попадут в результат**. 64 | 65 | 🔥 Теперь запрос вернет товары с самой высокой ценой в каждой категории! 🚀 66 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-min-max-salary-by-unit.md: -------------------------------------------------------------------------------- 1 | #### 15. Минимальная и максимальная зарплата по отделам без уволенных 2 | 3 | **Условие задачи:** 4 | 📊 **Написать SQL-запрос, который вернет минимальную и максимальную зарплату по каждому отделу среди не уволенных сотрудников.** 5 | 6 | 7 | 📌 **Структура таблиц:** 8 | 9 | - `units (id, name)` – содержит подразделения компании 10 | - `employees (id, unit_id, salary, fired)` – содержит сотрудников и их зарплаты 11 | 12 | 🚀 **Требуется вернуть:** 13 | 14 | - `unit_id` 15 | - `min_salary` (минимальная зарплата среди не уволенных) 16 | - `max_salary` (максимальная зарплата среди не уволенных) 17 | 18 | 19 | 20 | {{< hint warning >}} 21 | **Спойлеры к решению**   22 | {{< /hint >}} 23 | 24 | {{< details "Подсказки" close >}} 25 | 💡 Фильтруем только не уволенных сотрудников (`fired = FALSE`). 26 | 💡 Группируем данные по `unit_id`. 27 | 💡 Используем агрегатные функции `MIN()` и `MAX()`. 28 | {{< /details >}} 29 | 30 | {{< details "Решение" close >}} 31 | 32 | ```sql 33 | SELECT 34 | unit_id, 35 | MIN(salary) AS min_salary, 36 | MAX(salary) AS max_salary 37 | FROM employees 38 | WHERE fired = FALSE 39 | GROUP BY unit_id; 40 | ``` 41 | 42 | ✅ **Объяснение решения:** 43 | 44 | - `WHERE fired = FALSE` – выбираем только работающих сотрудников. 45 | - `GROUP BY unit_id` – группируем данные по подразделению. 46 | - `MIN(salary), MAX(salary)` – вычисляем минимальную и максимальную зарплату в группе. 47 | 48 | 🔥 Теперь запрос вернет минимальную и максимальную зарплату по каждому подразделению среди работающих сотрудников! 🚀 49 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-min-max-salary.md: -------------------------------------------------------------------------------- 1 | #### 6. Необходимо написать sql запрос, который вернет минимальную и максимальную зарплату по всем отделам среди не уволенных сотрудников 2 | 3 | ``` 4 | В базе данных есть две таблицы: ""units"" - подразделения компании и ""employees"" - сотрудники компании 5 | units: 6 | id int // Primary Key 7 | name text // (название unit) 8 | employees: 9 | id int // Primary Key 10 | unit_id int // Foreign Key -> units::id 11 | salary numeric // (зарплата) 12 | fired boolean // (флаг уволен) 13 | 14 | ``` 15 | 16 | {{< hint warning >}} 17 | **Спойлеры к решению**   18 | {{< /hint >}} 19 | 20 | {{< details "Подсказки" close >}} 21 | 🔗 Нужно **объединить** таблицы `units` и `employees` по `unit_id`. 22 | ⚡ Отфильтровать **не уволенных** сотрудников (`fired = FALSE`). 23 | 📊 Использовать **`GROUP BY`**, чтобы агрегировать зарплаты по отделам. 24 | 📈 **`MIN()` и `MAX()`** помогут найти минимальную и максимальную зарплату. 25 | {{< /details >}} 26 | 27 | {{< details "Решение" close >}} 28 | 29 | ```sql 30 | SELECT 31 | u.id AS unit_id, 32 | u.name AS unit_name, 33 | MIN(e.salary) AS min_salary, 34 | MAX(e.salary) AS max_salary 35 | FROM units u 36 | JOIN employees e ON u.id = e.unit_id 37 | WHERE e.fired = FALSE 38 | GROUP BY u.id, u.name; 39 | ``` 40 | 41 | 📌 **Объяснение:** 42 | 43 | - **`JOIN employees e ON u.id = e.unit_id`** – связываем сотрудников с их отделами. 44 | - **`WHERE e.fired = FALSE`** – исключаем уволенных сотрудников. 45 | - **`MIN(e.salary)` и `MAX(e.salary)`** – вычисляем минимальную и максимальную зарплаты. 46 | - **`GROUP BY u.id, u.name`** – группируем данные по отделам. 47 | 48 | 🔥 **Вывод примера:** 49 | 50 | |unit_id|unit_name|min_salary|max_salary| 51 | |---|---|---|---| 52 | |1|IT|50000|150000| 53 | |2|HR|40000|90000| 54 | 55 | ✅ **Готово!** 🚀 56 | {{< /details >}} 57 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-missing-users.md: -------------------------------------------------------------------------------- 1 | #### 11. Написать запрос.Всего 10 пользователей. В выводе 5 пользователей в статусе Новый, 3 пользователя в статусе Работает. Куда делось еще 2 пользователя? 2 | 3 | ``` 4 | Сколько пользователей в каком статусе? 5 | Результат должен быть в виде: Название статуса, Количество пользователей в этом статусе 6 | 7 | User 8 | id 9 | name 10 | status_id 11 | 12 | Status 13 | id 14 | name 15 | 16 | ``` 17 | 18 | {{< hint warning >}} 19 | **Спойлеры к решению**   20 | {{< /hint >}} 21 | 22 | {{< details "Подсказки" close >}} 23 | 📊 Задача требует подсчета количества пользователей для каждого статуса. 24 | 🔍 Мы можем использовать **`JOIN`** для объединения таблиц **`User`** и **`Status`**, чтобы получить информацию о пользователях и их статусах. 25 | 🔑 Используем **`GROUP BY`** для группировки по статусу и подсчета количества пользователей в каждом статусе. 26 | {{< /details >}} 27 | 28 | {{< details "Решение" close >}} 29 | 30 | ```sql 31 | SELECT s.name AS status_name, COUNT(u.id) AS user_count 32 | FROM User u 33 | JOIN Status s ON u.status_id = s.id 34 | GROUP BY s.name; 35 | ``` 36 | 37 | 📌 **Объяснение:** 38 | 39 | - **`JOIN`** соединяет таблицы **`User`** и **`Status`** по полю **`status_id`**. 40 | - **`GROUP BY s.name`** группирует по названию статуса. 41 | - **`COUNT(u.id)`** подсчитывает количество пользователей в каждом статусе. 42 | 43 | 📊 **Результат:** 44 | 45 | - В выводе будут отображены статусы с количеством пользователей в этих статусах. Если пользователи не попали в вывод, это может означать, что они имеют статус, не представленный в запросе. Возможно, они имеют статус, который не был учтен в подсчете (например, был удален или имеет другой статус, не указанный в выводе). 46 | 47 | 🎯 **Задача решена!** 48 | {{< /details >}} 49 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-most-frequent-value.md: -------------------------------------------------------------------------------- 1 | #### 30. Самые повторяющиеся значения `value` 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблица с колонками `key` и `value`: 5 | ```text 6 | key | value 7 | ----+------- 8 | 1 | sdfs 9 | 2 | dsfg 10 | 3 | sdfs 11 | ``` 12 | 13 | Необходимо найти самое часто встречающееся значение в колонке `value`. 14 | 15 | {{< hint warning >}} 16 | **Спойлеры к решению** 17 | {{< /hint >}} 18 | 19 | {{< details "Подсказки" close >}} 20 | 💡 Используйте `GROUP BY value` для подсчёта количества вхождений каждого значения. 21 | 💡 Отсортируйте результат по убыванию счётчика (`COUNT(*) DESC`) и возьмите первую строку (`LIMIT 1`). 22 | 💡 Альтернативно примените оконную функцию `ROW_NUMBER()` или `RANK()`, чтобы выбрать значение с максимальным количеством. 23 | {{< /details >}} 24 | 25 | {{< details "Решение" close >}} 26 | 27 | ```sql 28 | -- Решение 1: GROUP BY + ORDER BY + LIMIT 29 | SELECT value 30 | FROM your_table 31 | GROUP BY value 32 | ORDER BY COUNT(*) DESC 33 | LIMIT 1; 34 | 35 | -- Решение 2: Оконная функция RANK() 36 | SELECT value 37 | FROM ( 38 | SELECT 39 | value, 40 | RANK() OVER (ORDER BY COUNT(*) DESC) AS rnk 41 | FROM your_table 42 | GROUP BY value 43 | ) t 44 | WHERE rnk = 1; 45 | ``` 46 | 47 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-multiple-cars-users.md: -------------------------------------------------------------------------------- 1 | #### 2. Написать SQL-запрос, чтобы вывести пользователей, у которых более одного автомобиля? 2 | 3 | 4 | ```sql 5 | CREATE TABLE USER ( 6 | id INT, 7 | name VARCHAR(50) 8 | ); 9 | 10 | INSERT INTO USER (id, name) VALUES 11 | (1, 'Ivan'), 12 | (2, 'Oleg'), 13 | (3, 'Anna'), 14 | (4, 'Ivan'), 15 | (5, 'Ted'); 16 | 17 | CREATE TABLE CAR ( 18 | id INT, 19 | model VARCHAR(50) 20 | ); 21 | 22 | INSERT INTO CAR (id, model) VALUES 23 | (4422, 'Opel 1'), 24 | (4523, 'BMV 5'), 25 | (4612, 'VW'), 26 | (4853, 'BMV 6'); 27 | 28 | ``` 29 | 30 | {{< hint warning >}} 31 | **Спойлеры к решению**   32 | {{< /hint >}} 33 | 34 | {{< details "Подсказки" close >}} 35 | - Нам нужно **посчитать количество машин** у каждого пользователя. 36 | - Используем `GROUP BY user_id` для группировки по пользователям. 37 | - Применяем `HAVING COUNT(*) > 1`, чтобы оставить только тех, у кого **больше одной машины**. 38 | - Убедимся, что таблица `CAR` связана с `USER`, добавив `user_id` в `CAR`. 39 | {{< /details >}} 40 | 41 | {{< details "Решение" close >}} 42 | 43 | Добавляем внешний ключ, если его нет: 44 | 45 | ```sql 46 | ALTER TABLE CAR ADD COLUMN user_id INT; 47 | ALTER TABLE CAR ADD FOREIGN KEY (user_id) REFERENCES USER(id); 48 | ``` 49 | 50 | Запрос для поиска пользователей с более чем одной машиной: 51 | 52 | ```sql 53 | SELECT u.id, u.name, COUNT(c.id) AS car_count 54 | FROM USER u 55 | JOIN CAR c ON u.id = c.user_id 56 | GROUP BY u.id, u.name 57 | HAVING COUNT(c.id) > 1; 58 | ``` 59 | 60 | 🔹 **Как это работает?** 61 | 62 | - `JOIN` объединяет таблицы `USER` и `CAR` по `user_id`. 63 | - `GROUP BY u.id, u.name` группирует данные по пользователям. 64 | - `COUNT(c.id)` считает количество машин у каждого пользователя. 65 | - `HAVING COUNT(c.id) > 1` оставляет только пользователей с **двумя и более машинами**. 66 | 67 | **Выход:** 68 | 69 | ``` 70 | id | name | car_count 71 | ---------------------- 72 | 1 | Ivan | 2 73 | ``` 74 | 75 | Значит, **у пользователя "Ivan" больше одной машины**. 🚗🚗 76 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-multiple-purchases-per-day.md: -------------------------------------------------------------------------------- 1 | #### 35. Пользователи с более чем одной покупкой в день 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблица `T1(date, user_id)` с датами покупок пользователей. 5 | Необходимо найти все случаи, когда пользователь совершил **более одной** покупки **в тот же день**, и вывести пары `(date, user_id)`. 6 | 7 | {{< hint warning >}} 8 | **Спойлеры к решению** 9 | {{< /hint >}} 10 | 11 | {{< details "Подсказки" close >}} 12 | 💡 Используйте `GROUP BY date, user_id` для агрегации покупок каждого пользователя по дате. 13 | 💡 Примените `HAVING COUNT(*) > 1` для фильтрации только тех групп, где покупок больше одной. 14 | 💡 Альтернативно — оконная функция `COUNT(*) OVER (PARTITION BY date, user_id)` и фильтрация по этому полю. 15 | {{< /details >}} 16 | 17 | {{< details "Решение" close >}} 18 | ```sql 19 | -- Решение 1: GROUP BY + HAVING 20 | SELECT 21 | date, 22 | user_id 23 | FROM T1 24 | GROUP BY 25 | date, 26 | user_id 27 | HAVING COUNT(*) > 1; 28 | 29 | -- Решение 2: Оконная функция 30 | SELECT DISTINCT 31 | date, 32 | user_id 33 | FROM ( 34 | SELECT 35 | date, 36 | user_id, 37 | COUNT(*) OVER (PARTITION BY date, user_id) AS cnt 38 | FROM T1 39 | ) sub 40 | WHERE cnt > 1; 41 | ``` 42 | 43 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-mutual-followers.md: -------------------------------------------------------------------------------- 1 | #### 36. Взаимные подписки (mutual follows) 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблица подписок в соцсети: 5 | ``` 6 | 7 | user_id | follower_id 8 | --------+------------ 9 | 1 | 5 10 | 1 | 4 11 | 2 | 3 12 | 3 | 2 13 | 5 | 1 14 | 15 | ``` 16 | 17 | Нужно найти пары пользователей, которые **фоловят друг друга** (не транзитивно), и вывести `(user_id, follower_id)`. 18 | 19 | {{< hint warning >}} 20 | **Спойлеры к решению** 21 | {{< /hint >}} 22 | 23 | {{< details "Подсказки" close >}} 24 | 💡 Используйте **само-джойн** таблицы на условии обратной подписки: `t1.user_id = t2.follower_id AND t1.follower_id = t2.user_id`. 25 | 💡 Альтернативно можно применить двойной `EXISTS`: проверка, что для данной пары существует обратная запись. 26 | 💡 Не забудьте исключить дубликаты, чтобы не выводить одну и ту же пару дважды (например, `(5,1)` и `(1,5)` оба не нужны). 27 | {{< /details >}} 28 | 29 | {{< details "Решение" close >}} 30 | ```sql 31 | -- Решение 1: SELF JOIN 32 | SELECT DISTINCT t1.user_id, 33 | t1.follower_id 34 | FROM follows t1 35 | JOIN follows t2 36 | ON t1.user_id = t2.follower_id 37 | AND t1.follower_id = t2.user_id 38 | WHERE t1.user_id < t1.follower_id; 39 | 40 | -- Решение 2: EXISTS 41 | SELECT f1.user_id, 42 | f1.follower_id 43 | FROM follows f1 44 | WHERE EXISTS ( 45 | SELECT 1 46 | FROM follows f2 47 | WHERE f2.user_id = f1.follower_id 48 | AND f2.follower_id = f1.user_id 49 | ) 50 | AND f1.user_id < f1.follower_id; 51 | ``` 52 | 53 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-n-last-news-with-comments.md: -------------------------------------------------------------------------------- 1 | #### 21. Получение последних N новостей и всех их комментариев 2 | 3 | 4 | **Условие задачи:** 5 | 📌 **Написать один SQL-запрос, который вернёт N последних новостей из таблицы `news` вместе со всеми их комментариями из таблицы `comments`.** 6 | 7 | - Таблица `news` имеет поля: `id` (integer auto_increment). 8 | - Таблица `comments` имеет поля: `id` (integer auto_increment), `news_id` (integer). 9 | 10 | {{< hint warning >}} 11 | **Спойлеры к решению** 12 | {{< /hint >}} 13 | 14 | {{< details "Подсказки" close >}} 15 | 💡 Можно воспользоваться подзапросом, который выбирает последние N новостей, а затем выполнить LEFT JOIN с таблицей `comments` для получения всех комментариев к выбранным новостям. 16 | 💡 Альтернативный вариант — использовать CTE (Common Table Expression) для отбора последних новостей, а затем присоединить к ним комментарии. 17 | {{< /details >}} 18 | 19 | {{< details "Решение" close >}} 20 | 21 | ```sql 22 | -- Решение 1: Использование подзапроса с LEFT JOIN 23 | SELECT n.*, c.* 24 | FROM ( 25 | SELECT * 26 | FROM news 27 | ORDER BY id DESC 28 | LIMIT N -- подставьте нужное количество новостей 29 | ) n 30 | LEFT JOIN comments c ON n.id = c.news_id; 31 | 32 | -- Решение 2: Использование CTE (Common Table Expression) 33 | WITH latest_news AS ( 34 | SELECT * 35 | FROM news 36 | ORDER BY id DESC 37 | LIMIT N -- подставьте нужное количество новостей 38 | ) 39 | SELECT ln.*, c.* 40 | FROM latest_news ln 41 | LEFT JOIN comments c ON ln.id = c.news_id; 42 | ```` 43 | 44 | ✅ **Объяснение решения:** 45 | 46 | - **Решение 1:** 47 | 48 | - Подзапрос `n` выбирает последние N записей из таблицы `news`, сортируя их по убыванию `id`. 49 | 50 | - LEFT JOIN с таблицей `comments` позволяет получить все комментарии, относящиеся к каждой новости. 51 | 52 | - **Решение 2:** 53 | 54 | - CTE `latest_news` определяется для выбора последних N новостей. 55 | 56 | - Основной запрос присоединяет комментарии по полю `news_id`, связывая их с новостями из CTE. 57 | 58 | 59 | 🔥 Оба решения позволяют получить нужные данные одним запросом, эффективно комбинируя новости и их комментарии! {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-names-occurring-more-than-5.md: -------------------------------------------------------------------------------- 1 | #### 29. Имена, встречающиеся более 5 раз 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблица `students(name text, "group" text, cource bigint)` в PostgreSQL. 5 | Необходимо посчитать и вывести те `name`, которые в таблице встречаются более 5 раз. 6 | 7 | {{< hint warning >}} 8 | **Спойлеры к решению** 9 | {{< /hint >}} 10 | 11 | {{< details "Подсказки" close >}} 12 | 💡 Используйте `GROUP BY name` для агрегирования по имени. 13 | 💡 Примените `HAVING COUNT(*) > 5` для фильтрации групп с более чем 5 вхождениями. 14 | 💡 Альтернативное решение — воспользоваться оконной функцией `COUNT(*) OVER (PARTITION BY name)` и обернуть в подзапрос с фильтром. 15 | {{< /details >}} 16 | 17 | {{< details "Решение" close >}} 18 | ```sql 19 | -- Решение 1: GROUP BY + HAVING 20 | SELECT name 21 | FROM students 22 | GROUP BY name 23 | HAVING COUNT(*) > 5; 24 | 25 | -- Решение 2: Оконная функция + DISTINCT 26 | SELECT DISTINCT name 27 | FROM ( 28 | SELECT 29 | name, 30 | COUNT(*) OVER (PARTITION BY name) AS name_count 31 | FROM students 32 | ) t 33 | WHERE name_count > 5; 34 | ``` 35 | 36 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-persons-payments.md: -------------------------------------------------------------------------------- 1 | #### 5. Даны две таблицы Persosn и Payments. Один-ко-многим. Справа описание. Напишите запрос, который выводит name и value 2 | 3 | ``` 4 | Даны две таблицы : 5 | Persons co списком работников 6 | 7 | id name 8 | 1 Petya 9 | 2 Vasya 10 | 3 Kolya 11 | 12 | Payments с зарплатными начислениями ежемесячно 13 | 14 | id persons_id value 15 | 1 1 10 16 | 2 1 20 17 | 3 3 15 18 | 19 | ``` 20 | 21 | 22 | 23 | {{< hint warning >}} 24 | **Спойлеры к решению**   25 | {{< /hint >}} 26 | 27 | {{< details "Подсказки" close >}} 28 | 📝 Нужно **объединить две таблицы** (`Persons` и `Payments`) по `persons_id`. 29 | 🔗 Используем **`JOIN`** для связи. 30 | 📊 Выводим **имя (`name`) и сумму `value` для каждого платежа**. 31 | {{< /details >}} 32 | 33 | {{< details "Решение" close >}} 34 | 35 | ```sql 36 | SELECT p.name, pay.value 37 | FROM Persons p 38 | JOIN Payments pay ON p.id = pay.persons_id; 39 | ``` 40 | 41 | 📌 **Объяснение:** 42 | 43 | - **`JOIN Persons p ON p.id = pay.persons_id`** связывает таблицы по `id`. 44 | - **Выбираем `p.name` и `pay.value`** – имя работника и его зарплату. 45 | 46 | 🔥 **Вывод результата**: 47 | 48 | |name|value| 49 | |---|---| 50 | |Petya|10| 51 | |Petya|20| 52 | |Kolya|15| 53 | 54 | ✅ **Простой и эффективный запрос** 🚀 55 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-pink-cars-and-color-count.md: -------------------------------------------------------------------------------- 1 | #### 32. Машины розового цвета и количество по цветам 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблицы: 5 | 6 | ```sql 7 | CREATE TABLE colors ( 8 | id INT NOT NULL PRIMARY KEY, 9 | name VARCHAR(50), 10 | UNIQUE(name) 11 | ); 12 | 13 | CREATE TABLE cars ( 14 | id INT NOT NULL PRIMARY KEY, 15 | name VARCHAR(50), 16 | color_id INT, 17 | FOREIGN KEY (color_id) REFERENCES colors(id) 18 | ); 19 | ``` 20 | 21 | 1. Вывести список всех машин розового цвета. 22 | 2. Посчитать количество машин для каждого цвета и вывести в формате: название цвета и количество машин. 23 | 24 | {{< hint warning >}} 25 | **Спойлеры к решению** 26 | {{< /hint >}} 27 | 28 | {{< details "Подсказки" close >}} 29 | 💡 Для первой задачи используйте `JOIN` и фильтрацию по `colors.name = 'pink'`. 30 | 💡 Для второй — агрегируйте по `colors.name` с помощью `GROUP BY` и `COUNT(*)`. 31 | 💡 Не забудьте применять `LEFT JOIN`, если нужно включить цвета без машин (опционально). 32 | {{< /details >}} 33 | 34 | {{< details "Решение" close >}} 35 | ```sql 36 | -- 1. Список машин розового цвета 37 | SELECT c.name AS car_name 38 | FROM cars c 39 | JOIN colors col ON c.color_id = col.id 40 | WHERE col.name = 'pink'; 41 | 42 | -- 2. Количество машин по каждому цвету 43 | SELECT col.name AS color, 44 | COUNT(c.id) AS car_count 45 | FROM colors col 46 | LEFT JOIN cars c ON c.color_id = col.id 47 | GROUP BY col.name 48 | ORDER BY col.name; 49 | ``` 50 | 51 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-profiles-more-than-10-posts.md: -------------------------------------------------------------------------------- 1 | #### 4. Выбери профили, у которых больше 10 постов 2 | 3 | ```sql 4 | -- Таблица profile 5 | CREATE TABLE profile ( 6 | id BIGSERIAL PRIMARY KEY, 7 | nickname VARCHAR, 8 | registered_at TIMESTAMP 9 | ); 10 | 11 | -- Таблица post 12 | CREATE TABLE post ( 13 | id BIGSERIAL PRIMARY KEY, 14 | owner_id BIGINT REFERENCES profile (id), 15 | body TEXT, 16 | inserted_at TIMESTAMP, 17 | likes_count INT 18 | ); 19 | 20 | -- Таблица subscription_count 21 | CREATE TABLE subscription_count ( 22 | profile_id BIGINT REFERENCES profile (id) UNIQUE, 23 | followers_count INT, 24 | following_count INT 25 | ); 26 | 27 | ``` 28 | 29 | 30 | {{< hint warning >}} 31 | **Спойлеры к решению**   32 | {{< /hint >}} 33 | 34 | {{< details "Подсказки" close >}} 35 | 📝 Нужно **подсчитать количество постов** у каждого пользователя и выбрать тех, у кого их больше 10. 36 | 🔗 Для этого **соединяем таблицу `profile` с `post` по `id`**. 37 | 📊 Используем **`GROUP BY` и `HAVING`** для фильтрации. 38 | {{< /details >}} 39 | 40 | {{< details "Решение" close >}} 41 | 42 | ```sql 43 | SELECT p.id, p.nickname 44 | FROM profile p 45 | JOIN post po ON p.id = po.owner_id 46 | GROUP BY p.id, p.nickname 47 | HAVING COUNT(po.id) > 10; 48 | ``` 49 | 50 | 📌 **Объяснение:** 51 | 52 | - **Соединяем `profile` и `post`** по `id` (соответствует `owner_id` в `post`). 53 | - **Группируем по `p.id, p.nickname`**, чтобы считать посты каждого профиля. 54 | - **Фильтруем `HAVING COUNT(po.id) > 10`**, оставляя только тех, у кого больше 10 постов. 55 | 56 | 🔥 **Преимущества:** 57 | ✅ Эффективный **подсчет через `COUNT`** 58 | ✅ **Фильтрация на уровне запроса**, а не в коде 59 | ✅ **Простая адаптация** (можно добавить другие условия, например, по дате) 🚀 60 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-select-top-5.md: -------------------------------------------------------------------------------- 1 | #### 31. Выбор пяти верхних записей 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблица с колонками `key` и `value`: 5 | ```text 6 | key | value 7 | ----+------- 8 | 1 | sdfs 9 | 2 | dsfg 10 | 3 | sdfs 11 | … | … 12 | ``` 13 | 14 | Необходимо выбрать **первые 5 записей** из этой таблицы. 15 | 16 | {{< hint warning >}} 17 | **Спойлеры к решению** 18 | {{< /hint >}} 19 | 20 | {{< details "Подсказки" close >}} 21 | 💡 В MySQL и PostgreSQL используйте `LIMIT 5`. 22 | 💡 В стандарте SQL можно применить `FETCH FIRST 5 ROWS ONLY`. 23 | 💡 В SQL Server используется `TOP 5`. 24 | {{< /details >}} 25 | 26 | {{< details "Решение" close >}} 27 | 28 | ```sql 29 | -- Решение 1 (MySQL, PostgreSQL): 30 | SELECT * 31 | FROM your_table 32 | LIMIT 5; 33 | 34 | -- Решение 2 (ANSI SQL): 35 | SELECT * 36 | FROM your_table 37 | FETCH FIRST 5 ROWS ONLY; 38 | 39 | -- Решение 3 (SQL Server): 40 | SELECT TOP 5 * 41 | FROM your_table; 42 | ``` 43 | 44 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-services-january-2021-week.md: -------------------------------------------------------------------------------- 1 | #### 24. Названия и цены услуг, проданных с 1 января 2021 по следующую неделю 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблицы: 5 | - **Services** (`id`, `name`, `cost`, `price`, `group_id`) 6 | - **Groups** (`id`, `name`) 7 | - **Orders** (`id`, `datetime`, `srv_id`) 8 | 9 | Необходимо вывести **название** и **розничную цену** (`price`) для всех услуг, которые продавались **1 января 2021 и в течение следующей недели**. 10 | 11 | {{< hint warning >}} 12 | **Спойлеры к решению** 13 | {{< /hint >}} 14 | 15 | {{< details "Подсказки" close >}} 16 | 💡 Соедините **Orders** с **Services** по `srv_id = Services.id`. 17 | 💡 Отфильтруйте по диапазону дат: от `'2021-01-01'` до `'2021-01-08'` (включительно). 18 | 💡 Используйте `DISTINCT`, чтобы каждая услуга выводилась единожды, даже если была продана несколько раз. 19 | 💡 Альтернативный вариант — сначала выбрать `srv_id` по фильтру дат, а затем присоединить **Services** через `IN` или `EXISTS`. 20 | {{< /details >}} 21 | 22 | {{< details "Решение" close >}} 23 | 24 | ```sql 25 | -- Решение 1: JOIN + DISTINCT + диапазон дат 26 | SELECT DISTINCT s.name, 27 | s.price 28 | FROM Services s 29 | JOIN Orders o ON o.srv_id = s.id 30 | WHERE o.datetime >= '2021-01-01' 31 | AND o.datetime < '2021-01-09'; 32 | ``` 33 | 34 | ```sql 35 | -- Решение 2: Использование подзапроса с IN 36 | SELECT s.name, 37 | s.price 38 | FROM Services s 39 | WHERE s.id IN ( 40 | SELECT o.srv_id 41 | FROM Orders o 42 | WHERE o.datetime BETWEEN '2021-01-01' AND '2021-01-08' 43 | ); 44 | ``` 45 | 46 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-students-names-c.md: -------------------------------------------------------------------------------- 1 | #### 3. Написать SQL-запрос для вывода имен студентов и университетов, где имена студентов начинаются на букву C? 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | - Для решения задачи нужно использовать SQL **`LIKE`** с шаблоном, чтобы фильтровать имена студентов, начинающиеся на букву "C". 10 | - Чтобы получить имена студентов и университетов, необходимо использовать **`JOIN`** для объединения двух таблиц: одной для студентов и другой для университетов. 11 | - Пример запроса будет зависеть от структуры таблиц, предположим, что есть таблицы **students** (студенты) и **universities** (университеты). 12 | - Нам нужно соединить таблицу студентов с таблицей университетов по какому-то общему ключу, например, по **university_id**. 13 | {{< /details >}} 14 | 15 | {{< details "Решение" close >}} 16 | 17 | Предположим, у нас есть две таблицы: 18 | 19 | ```sql 20 | CREATE TABLE universities ( 21 | id INT PRIMARY KEY, 22 | name VARCHAR(100) 23 | ); 24 | 25 | CREATE TABLE students ( 26 | id INT PRIMARY KEY, 27 | name VARCHAR(100), 28 | university_id INT, 29 | FOREIGN KEY (university_id) REFERENCES universities(id) 30 | ); 31 | ``` 32 | 33 | Запрос для вывода имен студентов и университетов, где имя студента начинается на "C": 34 | 35 | ```sql 36 | SELECT s.name AS student_name, u.name AS university_name 37 | FROM students s 38 | JOIN universities u ON s.university_id = u.id 39 | WHERE s.name LIKE 'C%'; 40 | ``` 41 | 42 | **Объяснение:** 43 | 44 | 1. **`SELECT s.name AS student_name, u.name AS university_name`** — выбираем имена студентов и университетов. 45 | 2. **`FROM students s`** — начинаем с таблицы студентов. 46 | 3. **`JOIN universities u ON s.university_id = u.id`** — выполняем соединение с таблицей университетов по полю **university_id**. 47 | 4. **`WHERE s.name LIKE 'C%'`** — фильтруем студентов, чьи имена начинаются на букву "C". Символ **`%`** означает любое количество символов после "C". 48 | 49 | **Ключевые моменты:** 50 | 51 | - Мы используем **`JOIN`** для объединения таблиц. 52 | - **`LIKE 'C%'`** позволяет фильтровать имена студентов, начинающиеся на "C". 53 | - Структура таблиц и ключи могут изменяться в зависимости от реальных данных, но основная логика запроса останется такой же. 54 | {{< /details >}} 55 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-top-3-visitors-by-visit-count.md: -------------------------------------------------------------------------------- 1 | #### 23. Топ-3 посетителя по числу визитов 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблица `visits(visit_id, visitor_name, timestamp)`. 5 | Необходимо вывести имена трёх посетителей, у которых наибольшее количество записей в таблице (визитов). 6 | 7 | {{< hint warning >}} 8 | **Спойлеры к решению** 9 | {{< /hint >}} 10 | 11 | {{< details "Подсказки" close >}} 12 | 💡 Используйте `GROUP BY visitor_name` для подсчёта визитов по каждому человеку. 13 | 💡 Отсортируйте результат по убыванию количества визитов (`COUNT(*) DESC`). 14 | 💡 Примените `LIMIT 3` для выборки трех записей. 15 | 💡 Альтернативно можно воспользоваться оконной функцией `ROW_NUMBER()` или `RANK()`. 16 | {{< /details >}} 17 | 18 | {{< details "Решение" close >}} 19 | 20 | ```sql 21 | -- Решение 1: GROUP BY + ORDER BY + LIMIT 22 | SELECT 23 | visitor_name, 24 | COUNT(*) AS visit_count 25 | FROM visits 26 | GROUP BY visitor_name 27 | ORDER BY visit_count DESC 28 | LIMIT 3; 29 | ``` 30 | 31 | ```sql 32 | -- Решение 2: Оконная функция ROW_NUMBER() 33 | SELECT visitor_name, visit_count 34 | FROM ( 35 | SELECT 36 | visitor_name, 37 | COUNT(*) OVER (PARTITION BY visitor_name) AS visit_count, 38 | ROW_NUMBER() OVER ( 39 | PARTITION BY visitor_name 40 | ORDER BY COUNT(*) OVER (PARTITION BY visitor_name) DESC 41 | ) AS rn 42 | FROM visits 43 | ) t 44 | WHERE rn = 1 45 | GROUP BY visitor_name, visit_count 46 | ORDER BY visit_count DESC 47 | LIMIT 3; 48 | ``` 49 | 50 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-top-sms-recipients.md: -------------------------------------------------------------------------------- 1 | #### 19. Топ-10 телефонов, получивших больше всего SMS 2 | 3 | **Условие задачи:** 4 | 📌 **Написать SQL-запрос для получения номеров телефонов (`phone`), которые получили более одной SMS.** 5 | 6 | - Отсортировать список по убыванию количества сообщений. 7 | - Оставить только **топ-10** номеров с наибольшим количеством полученных SMS. 8 | 9 | ```sql 10 | table: sms 11 | id | phone | message 12 | 1 | 9876552222 | some message 13 | 2 | 9876552222 | some message 14 | 3 | 9876552234 | another message 15 | 16 | ``` 17 | 18 | 19 | {{< hint warning >}} 20 | **Спойлеры к решению**   21 | {{< /hint >}} 22 | 23 | {{< details "Подсказки" close >}} 24 | 💡 Используй `GROUP BY`, чтобы сгруппировать данные по `phone`. 25 | 💡 `HAVING COUNT(*) > 1` поможет отфильтровать только те номера, у которых более одной SMS. 26 | 💡 `ORDER BY COUNT(*) DESC` обеспечит сортировку от большего количества к меньшему. 27 | 💡 `LIMIT 10` позволит оставить только топ-10 номеров. 28 | {{< /details >}} 29 | 30 | {{< details "Решение" close >}} 31 | 32 | ```sql 33 | SELECT phone, COUNT(*) AS sms_count 34 | FROM sms 35 | GROUP BY phone 36 | HAVING COUNT(*) > 1 37 | ORDER BY sms_count DESC 38 | LIMIT 10; 39 | ``` 40 | 41 | ✅ **Объяснение решения:** 42 | 43 | - **`GROUP BY phone`** – группируем записи по номерам телефонов. 44 | - **`COUNT(*) AS sms_count`** – считаем количество сообщений для каждого номера. 45 | - **`HAVING COUNT(*) > 1`** – фильтруем только те номера, у которых больше одной SMS. 46 | - **`ORDER BY sms_count DESC`** – сортируем по убыванию количества сообщений. 47 | - **`LIMIT 10`** – оставляем только 10 самых популярных получателей. 48 | 49 | 🔥 Теперь можно быстро найти топ-10 номеров, получивших больше всего SMS! 📲🚀 50 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-units-sold-after-time.md: -------------------------------------------------------------------------------- 1 | #### 1. Найди id юнитов, которые были проданы на сумму более 1000 рублей после 10:00 сегодняшнего дня. > "2024-06-03 10:00" 2 | 3 | 4 | ```sql 5 | -- Создание таблицы Unit 6 | CREATE TABLE Unit ( 7 | Id INT PRIMARY KEY, 8 | Price DECIMAL(10, 2) 9 | ); 10 | 11 | -- Создание таблицы Sales 12 | CREATE TABLE Sales ( 13 | Id INT PRIMARY KEY, 14 | Unit_id INT, 15 | Sale_time TIMESTAMP, 16 | FOREIGN KEY (Unit_id) REFERENCES Unit(Id) 17 | ); 18 | ``` 19 | 20 | {{< hint warning >}} 21 | **Спойлеры к решению**   22 | {{< /hint >}} 23 | 24 | {{< details "Подсказки" close >}} 25 | - Нужно выбрать `Unit.Id`, у которых сумма продаж превышает `1000` рублей. 26 | - Для фильтрации использовать `SUM(Price) > 1000` и `Sale_time > '2024-06-03 10:00'`. 27 | - Объединяем таблицы через `JOIN` по `Unit.Id = Sales.Unit_id`. 28 | - Используем `GROUP BY Unit_id`, чтобы агрегировать суммы. 29 | {{< /details >}} 30 | 31 | {{< details "Решение" close >}} 32 | 33 | ```sql 34 | SELECT u.Id 35 | FROM Unit u 36 | JOIN Sales s ON u.Id = s.Unit_id 37 | WHERE s.Sale_time > '2024-06-03 10:00' 38 | GROUP BY u.Id 39 | HAVING SUM(u.Price) > 1000; 40 | ``` 41 | 42 | Этот SQL-запрос выбирает идентификаторы юнитов (`Id`), у которых сумма продаж после 10:00 превышает 1000 рублей. ✅ 43 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-user-car-relationship.md: -------------------------------------------------------------------------------- 1 | #### 13. Пользователь и его машины: связь в базе данных 2 | 3 | **Условие задачи:** 4 | 🔍 **Создать структуру базы данных, где один пользователь может владеть несколькими машинами, а каждая машина принадлежит только одному пользователю.** 5 | 6 | 7 | ```sql 8 | -- Создаем таблицу USER 9 | CREATE TABLE USER ( 10 | id INT PRIMARY KEY, 11 | name VARCHAR(50) NOT NULL 12 | ); 13 | 14 | -- Вставляем данные в таблицу USER 15 | INSERT INTO USER (id, name) VALUES 16 | (1, 'Ivan'), 17 | (2, 'Oleg'), 18 | (3, 'Anna'), 19 | (4, 'Ivan'), 20 | (5, 'Ted'); 21 | 22 | -- Создаем таблицу CAR 23 | CREATE TABLE CAR ( 24 | id INT PRIMARY KEY, 25 | model VARCHAR(50) NOT NULL, 26 | user_id INT NOT NULL, 27 | FOREIGN KEY (user_id) REFERENCES USER(id) 28 | ); 29 | 30 | -- Вставляем данные в таблицу CAR 31 | INSERT INTO CAR (id, model, user_id) VALUES 32 | (4422, 'Opel 1', 1), 33 | (4523, 'BMV 5', 2), 34 | (4612, 'VW', 3), 35 | (4853, 'BMV 6', 4); 36 | 37 | ``` 38 | 39 | 40 | 41 | {{< hint warning >}} 42 | **Спойлеры к решению**   43 | {{< /hint >}} 44 | 45 | {{< details "Подсказки" close >}} 46 | 💡 В `CAR` должна быть внешняя связь (`FOREIGN KEY`) с `USER`, указывающая на владельца. 47 | 💡 Каждый `USER` может иметь несколько записей в `CAR`, но каждая `CAR` должна быть привязана к одному `USER`. 48 | 💡 Можно использовать `JOIN`, чтобы получить информацию о пользователях и их автомобилях. 49 | {{< /details >}} 50 | 51 | {{< details "Решение" close >}} 52 | 53 | ```sql 54 | -- Создаем таблицу пользователей 55 | CREATE TABLE USER ( 56 | id INT PRIMARY KEY, 57 | name VARCHAR(50) NOT NULL 58 | ); 59 | 60 | -- Заполняем таблицу пользователей 61 | INSERT INTO USER (id, name) VALUES 62 | (1, 'Ivan'), 63 | (2, 'Oleg'), 64 | (3, 'Anna'), 65 | (4, 'Ivan'), 66 | (5, 'Ted'); 67 | 68 | -- Создаем таблицу автомобилей 69 | CREATE TABLE CAR ( 70 | id INT PRIMARY KEY, 71 | model VARCHAR(50) NOT NULL, 72 | user_id INT NOT NULL, 73 | FOREIGN KEY (user_id) REFERENCES USER(id) ON DELETE CASCADE 74 | ); 75 | 76 | -- Заполняем таблицу автомобилей 77 | INSERT INTO CAR (id, model, user_id) VALUES 78 | (4422, 'Opel 1', 1), 79 | (4523, 'BMW 5', 2), 80 | (4612, 'VW', 3), 81 | (4853, 'BMW 6', 4); 82 | 83 | -- Запрос для получения списка пользователей и их машин 84 | SELECT 85 | u.id AS user_id, 86 | u.name AS user_name, 87 | c.id AS car_id, 88 | c.model AS car_model 89 | FROM USER u 90 | LEFT JOIN CAR c ON u.id = c.user_id 91 | ORDER BY u.id; 92 | ``` 93 | 94 | ✅ **Объяснение решения:** 95 | 96 | - Создаем таблицу `USER`, где `id` — первичный ключ. 97 | - Создаем таблицу `CAR`, где `user_id` — внешний ключ, связывающий машину с пользователем. 98 | - Добавляем `ON DELETE CASCADE`, чтобы при удалении пользователя удалялись и его машины. 99 | - `LEFT JOIN` позволяет вывести всех пользователей, даже если у них нет машины. 100 | - `ORDER BY u.id` упорядочивает результат по пользователям. 101 | 102 | 🚀 **Теперь можно легко получать информацию о пользователях и их машинах!** 🔥 103 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-users-multiple-cars.md: -------------------------------------------------------------------------------- 1 | #### 14. Поиск пользователей, у которых больше одной машины 2 | 3 | **Условие задачи:** 4 | 🚗 **Вывести имена пользователей, у которых более одного автомобиля.** 5 | 6 | ```sql 7 | -- Создаем таблицу USER 8 | CREATE TABLE USER ( 9 | id INT PRIMARY KEY, 10 | name VARCHAR(50) NOT NULL 11 | ); 12 | 13 | -- Вставляем данные в таблицу USER 14 | INSERT INTO USER (id, name) VALUES 15 | (1, 'Ivan'), 16 | (2, 'Oleg'), 17 | (3, 'Anna'), 18 | (4, 'Ivan'), 19 | (5, 'Ted'); 20 | 21 | -- Создаем таблицу CAR 22 | CREATE TABLE CAR ( 23 | id INT PRIMARY KEY, 24 | model VARCHAR(50) NOT NULL, 25 | user_id INT NOT NULL, 26 | FOREIGN KEY (user_id) REFERENCES USER(id) 27 | ); 28 | 29 | -- Вставляем данные в таблицу CAR 30 | INSERT INTO CAR (id, model, user_id) VALUES 31 | (4422, 'Opel 1', 1), 32 | (4523, 'BMV 5', 2), 33 | (4612, 'VW', 3), 34 | (4853, 'BMV 6', 4); 35 | ``` 36 | 37 | 38 | 39 | {{< hint warning >}} 40 | **Спойлеры к решению**   41 | {{< /hint >}} 42 | 43 | {{< details "Подсказки" close >}} 44 | 💡 Нам нужно сгруппировать данные по `user_id`. 45 | 💡 Используем `HAVING` для фильтрации пользователей, у которых количество автомобилей (`COUNT(user_id)`) больше 1. 46 | 💡 Присоединяем таблицу `USER`, чтобы получить имена. 47 | {{< /details >}} 48 | 49 | {{< details "Решение" close >}} 50 | 51 | ```sql 52 | SELECT u.name 53 | FROM USER u 54 | JOIN CAR c ON u.id = c.user_id 55 | GROUP BY u.id, u.name 56 | HAVING COUNT(c.id) > 1; 57 | ``` 58 | 59 | ✅ **Объяснение решения:** 60 | 61 | - Используем `JOIN`, чтобы соединить пользователей и их автомобили. 62 | - Группируем результат по `user_id`, так как нам нужно считать количество машин у каждого пользователя. 63 | - `HAVING COUNT(c.id) > 1` фильтрует только тех, у кого машин больше одной. 64 | 65 | 🔥 Теперь запрос вернет список пользователей, у которых больше одного автомобиля! 🚀 66 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-users-without-orders.md: -------------------------------------------------------------------------------- 1 | #### 20. Пользователи без заказов 2 | 3 | 4 | **Условие задачи:** 5 | 📌 **Написать SQL-запрос для получения всех `id` пользователей из таблицы `users`, у которых отсутствуют заказы в таблице `orders`.** 6 | 7 | - Таблица `users` содержит поле `id`. 8 | - Таблица `orders` содержит поля `userId` и `orderId`. 9 | - Задача — найти всех пользователей, у которых нет связанных записей в `orders`. 10 | 11 | {{< hint warning >}} 12 | **Спойлеры к решению** 13 | {{< /hint >}} 14 | 15 | {{< details "Подсказки" close >}} 16 | 💡 Для решения задачи можно использовать конструкцию `LEFT JOIN` и фильтровать по NULL. 17 | 💡 Альтернативное решение — применить условие `NOT EXISTS` для исключения пользователей с заказами. 18 | 💡 Оба подхода позволяют корректно выбрать пользователей, у которых нет записей в таблице `orders`. 19 | {{< /details >}} 20 | 21 | {{< details "Решение" close >}} 22 | 23 | ```sql 24 | -- Решение 1: Использование LEFT JOIN 25 | SELECT u.id 26 | FROM users u 27 | LEFT JOIN orders o ON u.id = o.userId 28 | WHERE o.userId IS NULL; 29 | 30 | -- Решение 2: Использование NOT EXISTS 31 | SELECT u.id 32 | FROM users u 33 | WHERE NOT EXISTS ( 34 | SELECT 1 35 | FROM orders o 36 | WHERE o.userId = u.id 37 | ); 38 | ```` 39 | 40 | ✅ **Объяснение решения:** 41 | 42 | - **Решение 1:** 43 | Запрос выполняет LEFT JOIN таблицы `users` с таблицей `orders`. Если для пользователя не найдено соответствующей записи, значение `o.userId` будет NULL, что и используется в условии WHERE для выборки таких пользователей. 44 | 45 | - **Решение 2:** 46 | Используется подзапрос с конструкцией `NOT EXISTS`, который проверяет отсутствие записей в таблице `orders` для каждого пользователя из таблицы `users`. 47 | 48 | 49 | 🔥 Теперь можно эффективно находить всех пользователей без заказов! {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-sql-users-without-trips.md: -------------------------------------------------------------------------------- 1 | #### 28. Пользователи без поездок 2 | 3 | **Условие задачи:** 4 | 📌 Есть таблицы: 5 | 6 | ```sql 7 | 8 | create table users ( 9 | id int8 primary key, 10 | name text, 11 | email text 12 | ); 13 | 14 | create table trips ( 15 | id int8 primary key, 16 | scooter int8, 17 | user_id int8, 18 | started_at timestamptz, 19 | finished_at timestamptz 20 | ); 21 | 22 | create table pauses ( 23 | id int8 primary key, 24 | trip_id int8, 25 | started_at timestamptz, 26 | finished_at timestamptz 27 | ); 28 | 29 | ``` 30 | 31 | Нужно вывести `id` всех пользователей, у которых нет ни одной поездки в таблице `trips`. 32 | 33 | {{< hint warning >}} 34 | **Спойлеры к решению** 35 | {{< /hint >}} 36 | 37 | {{< details "Подсказки" close >}} 38 | 💡 Используйте `LEFT JOIN` между `users` и `trips`, фильтруя по `trips.user_id IS NULL`. 39 | 💡 Альтернативно, примените `NOT EXISTS` с подзапросом, проверяющим отсутствие записей в `trips` для каждого пользователя. 40 | {{< /details >}} 41 | 42 | {{< details "Решение" close >}} 43 | ```sql 44 | -- Решение 1: LEFT JOIN + фильтрация по NULL 45 | SELECT u.id 46 | FROM users u 47 | LEFT JOIN trips t ON u.id = t.user_id 48 | WHERE t.user_id IS NULL; 49 | 50 | -- Решение 2: NOT EXISTS 51 | SELECT u.id 52 | FROM users u 53 | WHERE NOT EXISTS ( 54 | SELECT 1 55 | FROM trips t 56 | WHERE t.user_id = u.id 57 | ); 58 | ``` 59 | 60 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-stream-avoid-complex-lambdas.md: -------------------------------------------------------------------------------- 1 | #### 2. Упростить вложенные лямбда-выражения 2 | 3 | **Условие задачи:** 4 | 📌 Разобраться, что делает данный код, и улучшить читаемость за счёт рефакторинга. 5 | 6 | ```java 7 | public class AvoidComplexLambdas { 8 | private final Set users = new HashSet<>(); 9 | 10 | public Set findEditors() { 11 | return users.stream() 12 | .filter(u -> u.getRoles().stream() 13 | .anyMatch(r -> r.getPermissions().contains(Permission.EDIT))) 14 | .collect(toSet()); 15 | } 16 | } 17 | ``` 18 | 19 | --- 20 | 21 | {{< hint warning >}} 22 | **Спойлеры к ответу** 23 | {{< /hint >}} 24 | 25 | {{< details "Подсказки" close >}} 26 | 💡 Метод `findEditors()` ищет пользователей, которые имеют хотя бы одну роль с разрешением `EDIT`. 27 | 💡 Вложенные лямбды ухудшают читаемость. 28 | 💡 Хорошей практикой будет **вынести вложенную логику** в отдельный метод с понятным названием. 29 | {{< /details >}} 30 | 31 | {{< details "Решение" close >}} 32 | 33 | ### 🔍 Что делает код? 34 | 35 | Метод `findEditors()`: 36 | 37 | - Проходит по всем `users`, 38 | 39 | - Фильтрует тех, у кого хотя бы одна `Role` содержит разрешение `Permission.EDIT`, 40 | 41 | - Возвращает подходящих пользователей как `Set`. 42 | 43 | 44 | ### ✅ Как улучшить? 45 | 46 | Выносим вложенные лямбды в отдельный метод `hasEditPermission(User u)`: 47 | 48 | ```java 49 | public class AvoidComplexLambdas { 50 | private final Set users = new HashSet<>(); 51 | 52 | public Set findEditors() { 53 | return users.stream() 54 | .filter(this::hasEditPermission) 55 | .collect(Collectors.toSet()); 56 | } 57 | 58 | private boolean hasEditPermission(User user) { 59 | return user.getRoles().stream() 60 | .anyMatch(role -> role.getPermissions().contains(Permission.EDIT)); 61 | } 62 | } 63 | ``` 64 | 65 | ### ✨ Преимущества: 66 | 67 | - Читаемость повышается. 68 | 69 | - Появляется возможность переиспользовать метод `hasEditPermission`. 70 | 71 | - Упрощается отладка и тестирование. 72 | 73 | 74 | 🔥 Рефакторинг — это про чистый код, а не только про короткий код! 🧹 75 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-stream-unique-active-item-names.md: -------------------------------------------------------------------------------- 1 | #### 1. Создать уникальную коллекцию (типа String) активных (атрибут Item.active со значением true) имен (атрибут Item.name), используя в качестве входных данных список items 2 | 3 | ```java 4 | @Getter 5 | @Setter 6 | class Item { 7 | private final Long id; 8 | private String name; 9 | private boolean active; 10 | } 11 | 12 | List items = ... 13 | 14 | 15 | Collection result = items.stream()... 16 | 17 | ``` 18 | 19 | 20 | {{< hint warning >}} 21 | **Спойлеры к решению**   22 | {{< /hint >}} 23 | 24 | {{< details "Подсказки" close >}} 25 | 💡 Мы можем использовать **Stream API** в Java для фильтрации элементов из списка и сбора результатов в уникальную коллекцию. 26 | 🔍 Для этого воспользуемся **`filter`** для выбора активных элементов и **`map`** для извлечения имен. Затем используем **`Collectors.toSet()`** для того, чтобы результат был уникальным. 27 | {{< /details >}} 28 | 29 | {{< details "Решение" close >}} 30 | 31 | ```java 32 | import java.util.*; 33 | import java.util.stream.*; 34 | 35 | @Getter 36 | @Setter 37 | class Item { 38 | private final Long id; 39 | private String name; 40 | private boolean active; 41 | } 42 | 43 | public class Main { 44 | public static void main(String[] args) { 45 | List items = ...; // Ваш список объектов Item 46 | 47 | Collection result = items.stream() 48 | .filter(Item::isActive) // Фильтруем активные элементы 49 | .map(Item::getName) // Извлекаем имя 50 | .collect(Collectors.toSet()); // Собираем в уникальную коллекцию 51 | 52 | System.out.println(result); 53 | } 54 | } 55 | ``` 56 | 57 | 📌 **Объяснение:** 58 | 59 | - **`filter(Item::isActive)`** — фильтруем элементы, где **`active == true`**. 60 | - **`map(Item::getName)`** — извлекаем имена активных элементов. 61 | - **`collect(Collectors.toSet())`** — собираем результат в **`Set`**, который автоматически исключает дубликаты, обеспечивая уникальность. 62 | 63 | 🚀 **Результат:** 64 | 65 | - Мы получим коллекцию уникальных имен активных предметов. 66 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-thread-sync-tick-tock.md: -------------------------------------------------------------------------------- 1 | #### 5. Реализовать код для отображения "Тик" и "Так" с разницей в одну секунду, используя два потока, с синхронизацией потоков в Java? 2 | 3 | 4 | {{< hint warning >}} 5 | **Спойлеры к решению**   6 | {{< /hint >}} 7 | 8 | {{< details "Подсказки" close >}} 9 | - Для синхронизации потоков можно использовать **wait()** и **notify()** или **notifyAll()**. 10 | - Один поток должен печатать "Тик", а другой — "Так". 11 | - Потоки должны работать с разницей в одну секунду. 12 | - Используйте общий объект или блок синхронизации для координации потоков. 13 | {{< /details >}} 14 | 15 | {{< details "Решение" close >}} 16 | 17 | Вот пример кода для реализации задачи: 18 | 19 | ```java 20 | public class TickTock { 21 | private final Object lock = new Object(); 22 | private boolean tickTurn = true; // Флаг для управления порядком 23 | 24 | public void tick() { 25 | synchronized (lock) { 26 | while (!tickTurn) { // Если очередь не для "Тика", ждем 27 | try { 28 | lock.wait(); 29 | } catch (InterruptedException e) { 30 | Thread.currentThread().interrupt(); 31 | } 32 | } 33 | System.out.println("Тик"); 34 | tickTurn = false; // Смена очередности 35 | lock.notify(); // Будим другой поток 36 | } 37 | } 38 | 39 | public void tock() { 40 | synchronized (lock) { 41 | while (tickTurn) { // Если очередь не для "Така", ждем 42 | try { 43 | lock.wait(); 44 | } catch (InterruptedException e) { 45 | Thread.currentThread().interrupt(); 46 | } 47 | } 48 | System.out.println("Так"); 49 | tickTurn = true; // Смена очередности 50 | lock.notify(); // Будим другой поток 51 | } 52 | } 53 | 54 | public static void main(String[] args) { 55 | TickTock tickTock = new TickTock(); 56 | 57 | Thread tickThread = new Thread(() -> { 58 | for (int i = 0; i < 5; i++) { 59 | tickTock.tick(); 60 | try { 61 | Thread.sleep(1000); // Задержка 1 секунда 62 | } catch (InterruptedException e) { 63 | Thread.currentThread().interrupt(); 64 | } 65 | } 66 | }); 67 | 68 | Thread tockThread = new Thread(() -> { 69 | for (int i = 0; i < 5; i++) { 70 | tickTock.tock(); 71 | try { 72 | Thread.sleep(1000); // Задержка 1 секунда 73 | } catch (InterruptedException e) { 74 | Thread.currentThread().interrupt(); 75 | } 76 | } 77 | }); 78 | 79 | tickThread.start(); 80 | tockThread.start(); 81 | } 82 | } 83 | ``` 84 | 85 | **Объяснение:** 86 | 87 | 1. **Общий объект lock**: используется для синхронизации потоков. 88 | 2. **Флаг tickTurn**: управляет порядком вывода "Тик" и "Так". 89 | 3. Потоки вызывают метод `tick()` или `tock()`, проверяя флаг `tickTurn`. 90 | 4. Потоки используют `wait()` для ожидания своей очереди и `notify()` для пробуждения другого потока. 91 | 5. Потоки выводят "Тик" или "Так" с задержкой в 1 секунду (`Thread.sleep(1000)`), обеспечивая интервал между выводами. 92 | 93 | Код выводит: 94 | 95 | ``` 96 | Тик 97 | Так 98 | Тик 99 | Так 100 | Тик 101 | Так 102 | Тик 103 | Так 104 | Тик 105 | Так 106 | ``` 107 | {{< /details >}} 108 | -------------------------------------------------------------------------------- /content/Задачи/livecoding/task-livecoding-top-five-integers.md: -------------------------------------------------------------------------------- 1 | #### 15. Создать интерфейс с 2 методами. Написать класс, реализующий интерфейс для Integer. В класс можем бесконечно отправлять int-ы. И в любой момент можем вызвать getTopFive() и получить 5 максимальных чисел из тех, что отправили. Можно гуглить. Надо будет запустить, проверить работу 2 | 3 | ```java 4 | 5 | interface I1{ 6 | void putValue(T t); 7 | List getTopFive(); 8 | } 9 | ``` 10 | 11 | {{< hint warning >}} 12 | **Спойлеры к решению**   13 | {{< /hint >}} 14 | 15 | {{< details "Подсказки" close >}} 16 | 🔹 Нам нужно хранить числа и уметь извлекать 5 максимальных. 17 | 🔹 Подойдет `PriorityQueue`, так как это минимальная куча (min-heap). 18 | 🔹 Размер очереди ограничим 5 элементами, удаляя меньшие значения. 19 | 🔹 Используем `List` для хранения результатов. 20 | {{< /details >}} 21 | 22 | {{< details "Решение" close >}} 23 | 24 | ```java 25 | import java.util.*; 26 | 27 | interface I1 { 28 | void putValue(T t); 29 | List getTopFive(); 30 | } 31 | 32 | class IntegerTopFive implements I1 { 33 | private final PriorityQueue minHeap = new PriorityQueue<>(); 34 | 35 | @Override 36 | public void putValue(T t) { 37 | if (t instanceof Integer) { 38 | int value = (Integer) t; 39 | minHeap.offer(value); 40 | if (minHeap.size() > 5) { 41 | minHeap.poll(); // Удаляем минимальный элемент, если больше 5 42 | } 43 | } else { 44 | throw new IllegalArgumentException("Only integers are allowed"); 45 | } 46 | } 47 | 48 | @Override 49 | public List getTopFive() { 50 | List result = new ArrayList<>(minHeap); 51 | result.sort(Collections.reverseOrder()); // Сортируем по убыванию 52 | return result; 53 | } 54 | 55 | public static void main(String[] args) { 56 | IntegerTopFive topFive = new IntegerTopFive(); 57 | 58 | topFive.putValue(10); 59 | topFive.putValue(5); 60 | topFive.putValue(20); 61 | topFive.putValue(3); 62 | topFive.putValue(8); 63 | topFive.putValue(25); 64 | topFive.putValue(15); 65 | 66 | System.out.println(topFive.getTopFive()); // [25, 20, 15, 10, 8] 67 | } 68 | } 69 | ``` 70 | 71 | 📌 **Объяснение:** 72 | 73 | - **`PriorityQueue`** хранит элементы в отсортированном порядке (минимальный в начале). 74 | - **Метод `putValue(T t)`** принимает `Integer`, добавляет в очередь, а если размер > 5, удаляет минимальный элемент. 75 | - **Метод `getTopFive()`** копирует элементы, сортирует по убыванию и возвращает. 76 | - **Пример вызова** показывает, как корректно хранятся 5 максимальных чисел. 77 | 78 | 🔥 **Преимущества этого решения:** 79 | ✅ Работает за **O(log 5) ≈ O(1)** для вставки и **O(5 log 5) ≈ O(5)** для сортировки. 80 | ✅ Поддерживает **динамическое обновление** максимальных 5 чисел. 81 | 82 | 🚀 Можно расширить для других типов, но в данном задании требуется `Integer`. 83 | {{< /details >}} -------------------------------------------------------------------------------- /content/Задачи/main-livecoding-stream.md: -------------------------------------------------------------------------------- 1 | # Stream API 2 | 3 | --- 4 | 5 | #### 1. [Создать уникальную коллекцию (типа String) активных (атрибут Item.active со значением true) имен (атрибут Item.name), используя в качестве входных данных список items](Задачи/livecoding/task-livecoding-stream-unique-active-item-names.md) 6 | 7 | #### 2. [Упростить вложенные лямбда-выражения](Задачи/livecoding/task-livecoding-stream-avoid-complex-lambdas.md) -------------------------------------------------------------------------------- /content/Основы Java/Collections/Collections.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/content/Основы Java/Collections/Collections.png -------------------------------------------------------------------------------- /content/Основы Java/Exceptions/Exception.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/content/Основы Java/Exceptions/Exception.png -------------------------------------------------------------------------------- /content/Основы Java/Multithreading/Life_Cycle_of_Thread.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/content/Основы Java/Multithreading/Life_Cycle_of_Thread.png -------------------------------------------------------------------------------- /content/Основы Java/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Основы Java' 3 | weight = 10 4 | bookFlatSection = true 5 | +++ 6 | 7 | # Язык программирования Java 8 | 9 | **Java** — это мощный и универсальный язык программирования, который используется для создания как простых приложений, так и сложных распределенных систем. 10 | 11 | Мы структурировали язык Java, выделив в нём несколько основных модулей: 12 | 13 | 1. **Java Core** — базовые элементы языка, включающие типы данных, ключевые слова, операторы и работу с объектами. Это фундамент, на котором строятся остальные модули. 14 | 15 | 2. **Collections** — коллекции представляют собой мощные структуры данных для хранения и управления множеством объектов. Они обеспечивают удобные инструменты для работы с массивами, списками, множествами и картами. 16 | 17 | 3. **Exceptions** — механизм обработки исключений, позволяющий эффективно обрабатывать ошибки и предотвращать сбои программы. Он делает код более надёжным и поддерживаемым. 18 | 19 | 4. **Generics** — обобщения помогают создавать type-safe классы и методы, минимизируя ошибки времени выполнения и повышая читаемость и гибкость кода. 20 | 21 | 5. **Functional Interface** — функциональные интерфейсы позволяют использовать лямбда-выражения, что делает код более компактным и выразительным, особенно при работе с функциональными стилями программирования. 22 | 23 | 6. **Stream API** — API для работы с потоками данных, который упрощает обработку больших объемов данных, обеспечивая гибкость и поддерживая такие операции, как фильтрация, сортировка и агрегирование. 24 | 25 | 7. **Multithreading** — многопоточность обеспечивает параллельное выполнение задач, позволяя программе работать более эффективно, особенно на многоядерных процессорах. Этот модуль включает управление потоками, синхронизацию и безопасность потоков. 26 | 27 | Каждый из этих модулей играет ключевую роль в Java-разработке и помогает создать мощный и удобный для поддержки код. 28 | 29 | -------------------------------------------------------------------------------- /content/Принципы и методы проектирования/SOLID.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'SOLID' 3 | weight = 1 4 | bookFlatSection = true 5 | +++ 6 | 7 | ## SOLID 8 | 9 | #### 1. Что такое SOLID(каждая буква)? 10 | **SOLID** – это принципы разработки программного обеспечения, следуя которым получаем хороший код, который в дальнейшем будет хорошо масштабироваться и поддерживаться в рабочем состоянии. 11 | 12 | **S** Single Responsibility Principle – принцип единственной ответственности.Каждый класс должен иметь только одну зону ответственности. 13 | 14 | **O** Open closed Principle – принцип открытости-закрытости.Классы должны быть открыты для расширения, но закрыты для изменения. 15 | 16 | **L** Liskov substitution Principle – принцип подстановки Барбары Лисков. Должна быть возможность вместо базового (родительского) типа класса подставить любой его подтип (класс-наследник), при этом работа программы не должна измениться. 17 | 18 | **I** Interface Segregation Principle – это принцип разделения интерфейсов. Данный принцип обозначает, что не нужно заставлять клиента (класс) реализовывать интерфейс, который не имеет к нему отношения. 19 | 20 | **D** Dependency Inversion Principle – это принцип инверсии зависимостей. Абстракции НЕ должны зависеть от деталей. Детали должны зависеть от абстракций. Модули верхнего уровня НЕ должны зависеть от модулей нижнего уровня, НО должны зависеть от абстракции. 21 | 22 | #### 2. Какие еще принципы можешь назвать? 23 | 1. **DRY** / Don't Repeat Yourself — Не повторяйся! 24 | 2. **KISS** / Keep It Simple Stupid — Делай это проще 25 | 3. **OR** / Occam's Razor — Бритва Оккама 26 | 27 | #### 3. Когда мы можем нарушать принципы SOLID? 28 | 29 | Принципы SOLID являются руководством для написания гибкого, поддерживаемого и расширяемого кода, но бывают ситуации, когда их нарушение может быть оправдано. Рассмотрим такие случаи: 30 | - Оптимизация производительности 31 | - Прототипирование и быстрые итерации 32 | - Ограниченные ресурсы 33 | - Преемственность кода 34 | -------------------------------------------------------------------------------- /content/Принципы и методы проектирования/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Принципы и методы проектирования' 3 | weight = 20 4 | bookFlatSection = true 5 | +++ 6 | 7 | -------------------------------------------------------------------------------- /content/Прочее/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Прочее' 3 | weight = 70 4 | bookFlatSection = true 5 | +++ -------------------------------------------------------------------------------- /content/Работа с базами данных/Migrations.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Миграции' 3 | weight = 5 4 | bookFlatSection = true 5 | +++ 6 | 7 | ## Миграции 8 | 9 | #### 1. Liquibase. Что это и для чего? 10 | *Liquibase* — это инструмент для управления миграциями базы данных, который помогает разработчикам и администраторам баз данных поддерживать синхронизацию структуры базы данных с кодом приложения. Liquibase позволяет версионировать изменения в базе данных, отслеживать их и автоматически применять при обновлениях. 11 | 12 | --- 13 | #### 2. Какие записи создаёт liquibase во время миграции? 14 | 15 | Liquibase автоматически создаёт и использует несколько служебных таблиц для отслеживания состояния миграций в базе данных. 16 | 17 | Основные таблицы: 18 | 19 | 1. **DATABASECHANGELOG** 20 | 21 | - Хранит информацию о выполненных миграциях (changelog-файлах). 22 | - Структура таблицы: 23 | 24 | |Поле|Описание| 25 | |---|---| 26 | |ID|Уникальный идентификатор изменения.| 27 | |AUTHOR|Автор миграции.| 28 | |FILENAME|Путь к файлу миграции (например, `changelog.xml`).| 29 | |DATEEXECUTED|Дата выполнения изменения.| 30 | |ORDEREXECUTED|Порядок выполнения изменений.| 31 | |MD5SUM|Хэш содержимого изменения для проверки.| 32 | 33 | 2. **DATABASECHANGELOGLOCK** 34 | 35 | - Предотвращает одновременное выполнение миграций разными процессами. 36 | - Структура таблицы: 37 | 38 | 39 | | Поле | Описание | 40 | | ----------- | -------------------------------------------------------------- | 41 | | ID | Идентификатор блокировки (обычно `1`). | 42 | | LOCKED | Флаг (`TRUE` или `FALSE`), показывающий, занята ли блокировка. | 43 | | LOCKGRANTED | Время предоставления блокировки. | 44 | | LOCKEDBY | Информация о процессе, который удерживает блокировку. | 45 | 46 | --- 47 | #### 3. Зачем нужны инструменты миграции? 48 | 49 | Инструменты миграции (например, Liquibase, Flyway) используются для управления изменениями в структуре базы данных в различных средах разработки. 50 | 51 | Основные цели: 52 | 53 | 1. **Автоматизация обновлений**: Автоматическое применение изменений базы данных (создание таблиц, добавление столбцов, индексов и т.д.). 54 | 2. **Отслеживание изменений**: Позволяет вести историю всех изменений базы (версионирование). 55 | 3. **Упрощение совместной работы**: Разработчики могут синхронизировать свои изменения через централизованные файлы миграции. 56 | 4. **Согласованность**: Изменения гарантированно применяются одинаково на всех окружениях (локально, тестовые, продакшн). 57 | 5. **Откат изменений**: Возможность отменить применённые миграции в случае ошибок. 58 | 6. **Безопасность**: Инструменты предотвращают выполнение миграций двумя процессами одновременно (через блокировки). 59 | 60 | 61 | -------------------------------------------------------------------------------- /content/Работа с базами данных/ORM/Hibernate_Entity_Life_cycle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/content/Работа с базами данных/ORM/Hibernate_Entity_Life_cycle.png -------------------------------------------------------------------------------- /content/Работа с базами данных/SQL/Analityc grouping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/content/Работа с базами данных/SQL/Analityc grouping.png -------------------------------------------------------------------------------- /content/Работа с базами данных/SQL/B-Tree structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/content/Работа с базами данных/SQL/B-Tree structure.png -------------------------------------------------------------------------------- /content/Работа с базами данных/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Работа с базами данных' 3 | weight = 30 4 | bookFlatSection = true 5 | +++ 6 | 7 | -------------------------------------------------------------------------------- /content/Развертывание и инфраструктура/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Развертывание и инфраструктура' 3 | weight = 60 4 | bookFlatSection = true 5 | +++ 6 | 7 | -------------------------------------------------------------------------------- /content/Тестирование/_index.md: -------------------------------------------------------------------------------- 1 | +++ 2 | title = 'Тестирование' 3 | weight = 50 4 | bookFlatSection = true 5 | +++ -------------------------------------------------------------------------------- /hugo.yaml: -------------------------------------------------------------------------------- 1 | baseURL: https://zhukovsd.github.io/java-backend-interview-prep/ 2 | title: IT Ментор | Java методичка 3 | languageCode: ru 4 | theme : hugo-book 5 | enableGitInfo: true 6 | 7 | params: 8 | BookTheme: 'auto' 9 | BookSection: '*' 10 | BookPortableLinks: true 11 | BookRepo: 'https://github.com/zhukovsd/java-backend-interview-prep' 12 | BookCommitPath: 'commit' 13 | BookEditPath: 'edit/master' 14 | 15 | menu: 16 | after: 17 | - name: "Telegram канал" 18 | url: "https://t.me/zhukovsd_it_mentor" 19 | weight: 10 20 | - name: "Telegram чат" 21 | url: "https://t.me/zhukovsd_it_chat" 22 | weight: 20 23 | - name: "YouTube" 24 | url: "https://www.youtube.com/@zhukovsd_it_mentor" 25 | weight: 30 26 | 27 | markup: 28 | tableOfContents: 29 | endLevel: 4 30 | ordered: false 31 | startLevel: 4 32 | 33 | services: 34 | googleAnalytics: 35 | id: "G-HSY357LTPQ" 36 | -------------------------------------------------------------------------------- /images/bot integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/images/bot integration.png -------------------------------------------------------------------------------- /images/questions popularity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhukovsd/java-backend-interview-prep/720369966fdf5562f251d9304f35751723750ade/images/questions popularity.png -------------------------------------------------------------------------------- /resources/_gen/assets/book.scss_b807c86e8030af4cdc30edccea379f5f.json: -------------------------------------------------------------------------------- 1 | {"Target":"book.min.f1efc9c8d2365d88b2e0b4ce45f3c4596b5b3eafedf4f0cb3c79bfbba2c5d70c.css","MediaType":"text/css","Data":{"Integrity":"sha256-8e/JyNI2XYiy4LTORfPEWWtbPq/t9PDLPHm/u6LF1ww="}} -------------------------------------------------------------------------------- /resources/_gen/assets/book.scss_e129fe35b8d0a70789c8a08429469073.json: -------------------------------------------------------------------------------- 1 | {"Target":"book.min.f1efc9c8d2365d88b2e0b4ce45f3c4596b5b3eafedf4f0cb3c79bfbba2c5d70c.css","MediaType":"text/css","Data":{"Integrity":"sha256-8e/JyNI2XYiy4LTORfPEWWtbPq/t9PDLPHm/u6LF1ww="}} --------------------------------------------------------------------------------