Java for Beginner
870 subscribers
1.01K photos
275 videos
14 files
1.69K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Что выведет код?

import java.util.*;

public class Task230126 {
public static void main(String[] args) {
List<String> list = Arrays.asList("apple", "banana", "grape", "orange");
int index1 = Collections.binarySearch(list, "grape");
int index2 = Collections.binarySearch(list, "kiwi");
System.out.println("Grape: " + index1);
System.out.println("Kiwi: " + index2);
}
}


#Tasks
Почему equals() без переопределения hashCode() — это баг? 🤓

Ответ:

Контракт Java требует: если equals() возвращает true, hashCode() обязан быть одинаковым.

Если переопределить только equals, HashMap и HashSet перестают работать корректно — объект может быть помещён в одну корзину, а искаться в другой. Это приводит к «потерянным» элементам, которые физически есть в коллекции, но логически недоступны. Ошибка сложно диагностируется, так как код компилируется и частично работает.

Это не вопрос стиля — это нарушение фундаментального контракта коллекций.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5
История IT-технологий сегодня — 24 января

ℹ️ Кто родился в этот день

Джон фон Не́йман (англ. John von Neumann /vɒn ˈnɔɪmən/; или Иоганн фон Нейман, нем. Johann von Neumann; при рождении Я́нош Ла́йош Нейман, венг. Neumann János Lajos, IPA: [nojmɒn ˈjaːnoʃ ˈlɒjoʃ]; 28 декабря 1903, Будапешт — 8 февраля 1957, Вашингтон) — венгеро-американский математик, физик и педагог еврейского происхождения, сделавший важный вклад в квантовую физику, квантовую логику, функциональный анализ, теорию множеств, информатику, экономику и другие отрасли науки. Наиболее известен как человек, с именем которого связывают архитектуру большинства современных компьютеров (так называемая архитектура фон Неймана), применение теории операторов к квантовой механике (алгебра фон Неймана), а также как участник Манхэттенского проекта и как создатель теории игр и концепции клеточных автоматов.

Ма́рвин Ли Ми́нский (англ. Marvin Lee Minsky; 9 августа 1927 — 24 января 2016) — американский учёный в области искусственного интеллекта, сооснователь Лаборатории искусственного интеллекта в Массачусетском технологическом институте.


🌐 Знаковые события

1984 — старт продаж Apple Macintosh 128K: первый массово успешный персональный компьютер с графическим интерфейсом и мышью; именно Mac популяризировал GUI и сильно повлиял на дизайн операционных систем, настольную полиграфию и массовое восприятие персональных компьютеров


#Biography #Birth_Date #Events #24Января
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Вот ранее большинство проголосовало за использование бусти. Допустим я в конец обнаглел и сделал это. Готовы ли вы платить за доступ?
Anonymous Poll
16%
Да, почему нет!
24%
Да ты охуел?
44%
Нет конечно же!
16%
Что я тут делаю?
3🔥1
История IT-технологий сегодня — 25 января

ℹ️ Кто родился в этот день

Семён Никола́евич Корса́ков (14 (25) января 1787 — 1 (13) декабря 1853)русский дворянин, изобретатель механических устройств, «интеллектуальных машин» для информационного поиска и классификации, пионер применения перфорированных карт в информатике. Известен также своими работами по гомеопатии.


🌐 Знаковые события

1701 — в Москве основана школа математических и навигацких наук.

1955 — учёные Колумбийского университета создали атомные часы, показывающие время с погрешностью 1 секунда в 300 лет.


1979 — первый задокументированный случай гибели человека от промышленного робота: робот на автомобильном заводе в США убивает рабочего; инцидент стал поворотным пунктом для обсуждения безопасности робототехники, стандартов, контроля и взаимодействия человека с автоматизированными системами.

2004 — на поверхность Марса совершил посадку второй американский марсоход «Оппортьюнити».


#Biography #Birth_Date #Events #25Января
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Потом не существует. Есть только сегодня и никогда

Очень знакомая и всеми любимая история:
“с понедельника не ем сладкое”,
“с нового года завязываю с курением”
.

Это не лень.

Это практичный обман твоего мозга: пообещать, а после того как ты успокоился — ничего не менять.

Но что если ты решил: Со следующей недели, бросаю CS и сажусь за Java?

Жаль тебя обламывать, но с началом недели ты снова запустишь привычную игру.
Потом пообещаешь начать со следующей.
Потом ещё раз.
И ещё.


Почему потом — это когнитивный баг, а не лень

Когда ты говоришь себе с понедельника начну, мозг получает мгновенную награду. Дофаминовый выброс происходит не от действия, а от планирования действия. Ты уже почувствовал себя организованным, дисциплинированным — и мозг доволен. Зачем реально менять что-то, если удовольствие уже получено?

Это называется intention-action gap (разрыв намерения и действия). Намерения объясняют только 30-40% вариативности поведения, а в экспериментальных условиях влияние намерения на реальное действие падает до 15% (Rhodes & Dickau, 2012; McEachan et al., 2011).

Цифры, которые показывают масштаб иллюзии:

- 43% бросают новогодние обещания к концу января, 81% — не доживают до марта. «Quitter's Day» (день массового бросания) приходится на вторую пятницу января — обычно это 19-20 число (Drive Research, 2024; Strava Data, 2019)

- 88% людей признаются, что регулярно откладывают важные долгосрочные задачи, предпочитая сиюминутные удовольствия (исследование Journal of Consumer Research).

- По данным опроса Stack Overflow, ~40% программистов регулярно откладывают изучение нового языка или технологии, несмотря на понимание их необходимости для карьеры.

- Формирование устойчивой новой привычки, по данным Европейского журнала социальной психологии требует в среднем за 59–66 дней (не 21, как миф), а у некоторых — до 254 дней (Lally et al., European Journal of Social Psychology, 2010; Scientific American, 2024). При этом только 23% людей достигают автоматичности поведения.


Вот мои советы:

1. Старт должен быть легчайшим

Не планируй с понедельника учить Java по 3 часа. Планируй: Сегодня открою IntelliJ и напишу public static void main, который выводит 'Hello World'. Всё.

Если после этого захочешь закрыть — закрывай. Но 90% вероятности, что продолжишь.

Почему это сработает: Когда действие занимает <2 минут, сопротивление минимально ("2-minute rule", Clear, 2018).


2. Окружение важнее мотивации

Убери иконку CS с рабочего стола. Прямо сейчас. Перемести в папку, до которой нужно лезть.

Установи Java JDK и IDE заранее, пока есть желание. Если завтра придётся тратить 20 минут на установку — 80% вероятности, что не начнёшь.

Заблокируй себе возможность залезть в соцсети и ютуб (за исключением этого канала конечно же)

Почему это сработает: структурированный подход повышает успешность формирования привычки на 64%.


3. Поставь что-то на кон

Делаем бездействие болезненным.

(На свой страх и риск)

Денежные ставки: Обещай другу, что если не сядешь за Java в течение 24 часов после объявления, переводишь ему 1000 рублей. Работает лучше мотивации.

Социальный контракт: Публично заяви в соцсетях, чате единомышленников или коллег: «Я приступаю к изучению Java с сегодняшнего дня. Каждую пятницу буду публиковать отчет о прогрессе и выложу первый проект ровно через месяц». Страх опозориться — мощнейший двигатель. Публичное обязательство повышает выполнение на 37%.

Implementation intention: Не "буду учить Java", а "завтра в 9:00, сразу после кофе, открою IntelliJ и создам новый класс". Конкретика времени и места снижает вероятность отказа на 50%.

Почему это сработает: Мозг боится потерь сильнее, чем любит приобретения (loss aversion (Kahneman & Tversky, 1979; Ruggeri et al., Nature Human Behaviour, 2020)). Используй это.


ПОЙМИ:

"Потом" — заканчивается ровно в тот момент, когда ты, прочитав это, не закрываешь статью, а открываешь новую вкладку и гуглишь "Java Hello World пример".

Не со следующей недели. Не завтра.

А прямо сейчас.


😎

#motivation
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍3🤯1🆒1
История IT-технологий сегодня — 26 января

ℹ️ Кто родился в этот день

Фе́ликс Хаусдо́рф (нем. Felix Hausdorff, 8 ноября 1868, Бреслау — 26 января 1942, Бонн) — математик, создавший современную теорию топологических и метрических пространств; топология и меры Хаусдорфа важны для анализа данных, фракталов и многих теоретических разделов информатики.


🌐 Знаковые события

1983 — выход Lotus 1‑2‑3 (по хронологии — 26 января): один из первых «киллер‑приложений» для IBM PC; электронная таблица, объединившая расчёты, графику и базу данных, сделала PC стандартом для бизнеса и показала, как одно приложение может радикально ускорить цифровизацию предприятий.


#Biography #Birth_Date #Events #26Января
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
3. Data Access Layer: осознанный выбор между JPA, JDBC и jOOQ

В этом видео мы разбираем один из самых фундаментальных архитектурных выборов в backend-разработке — подход к работе с базой данных.

Речь пойдёт не о синтаксисе и не о "как написать код", а о trade-offs, ответственности и стоимости владения каждого подхода.

Мы реализуем один и тот же use-case сохранения заказа тремя способами:
🔹 JDBC — полный контроль и максимальная ответственность
🔹 jOOQ — типобезопасный SQL и compile-time гарантии
🔹 JPA (Hibernate) — абстракция, скорость разработки и экосистема Spring
🔹 И немного дебага как всегда)

А затем осознанно выбираем JPA как production-стратегию для проекта OrderHub.

Исходный код проекта на GitHub очень ждет Ваших звезд.

Ссылка на Youtube
Ссылка на Рутьюб

Смотрите, ставьте лайки, подписывайтесь на каналы!✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍1
Глава 4: Эффективный поиск. Бинарный и не только

Модификации и родственные методы

Ранее мы рассмотрели классический бинарный поиск, который эффективно отвечает на вопрос «есть ли элемент X в массиве?» и возвращает случайную позицию в случае дубликатов. Но реальные задачи редко сводятся к такому простому запросу. Что делать, если вам нужно найти все книги с заданным названием в каталоге, где дубликаты вполне закономерны (например, несколько экземпляров одного издания)? А как насчет поиска диапазона — всех книг, выпущенных между 2020 и 2023 годом включительно? Или если вы знаете, что данные распределены не просто монотонно, а равномерно, и хотите использовать это свойство для ускорения?
Эти вопросы приводят нас к модификациям классического алгоритма и его альтернативам. Каждая модификация сохраняет логарифмическую природу, но меняет инварианты и условия завершения.


Поиск границ в массиве с дубликатами

Проблема неоднозначности стандартной реализации

Представьте массив книг, отсортированных по названию:

String[] titles = {
"Война и мир", "Война и мир", "Война и мир",
"Гарри Поттер", "Гарри Поттер",
"Мастер и Маргарита"
};


Вызываем Arrays.binarySearch(titles, "Война и мир"). Результат? Не определен. Javadoc гарантирует лишь, что будет возвращен какой-то индекс из диапазона дубликатов, если они существуют. Это недопустимо, когда вам нужен первый экземпляр для отображения в UI или последний для расчета границ диапазона.

Инвариант для поиска первого вхождения

Чтобы гарантированно найти самую левую позицию элемента, мы меняем логику завершения. Классический поиск завершается при точном совпадении array[mid] == target. Мы же должны продолжать поиск в левой половине даже после нахождения совпадения, потому что там может скрываться еще более ранний дубликат.

Новый инвариант: алгоритм поддерживает две зоны — переднюю, где все элементы строго меньше target, и заднюю, где элементы больше или равны target. Когда цикл завершается, указатель left будет указывать на первый элемент, равный target, или на позицию вставки, если target отсутствует.


#Java #для_новичков #beginner #algorithm #sorted #binary
👍3
public class BinarySearchBounds {

/**
* Поиск первого вхождения целевого значения.
* Если элемент отсутствует, возвращает индекс, где он должен быть вставлен.
*
* @return индекс первого вхождения target или potential insertion point
*/
public static int findFirst(int[] array, int target) {
if (array == null) throw new IllegalArgumentException("Array cannot be null");

int left = 0;
int right = array.length - 1;
int result = -1; // Потенциальная позиция вставки

while (left <= right) {
int mid = left + (right - left) / 2;
int midValue = array[mid];

if (midValue < target) {
// Цель строго правее
left = mid + 1;
} else if (midValue > target) {
// Цель строго левее
right = mid - 1;
} else {
// Нашли совпадение, но продолжаем искать в левой половине
result = mid; // Запомнили потенциальный ответ
right = mid - 1; // Сдвинули границу влево
}
}

// Если result остался -1, target не найден. Можно вернуть left как точку вставки.
return result != -1 ? result : left;
}

/**
* Поиск последнего вхождения целевого значения.
*/
public static int findLast(int[] array, int target) {
if (array == null) throw new IllegalArgumentException("Array cannot be null");

int left = 0;
int right = array.length - 1;
int result = -1;

while (left <= right) {
int mid = left + (right - left) / 2;
int midValue = array[mid];

if (midValue < target) {
left = mid + 1;
} else if (midValue > target) {
right = mid - 1;
} else {
result = mid; // Запомнили потенциальный ответ
left = mid + 1; // Ключевое отличие: сдвигаемся вправо
}
}

return result != -1 ? result : left - 1; // left указывает после последнего <= target
}
}


Ключевые отличия от классического поиска:
Сохранение состояния: Переменная result запоминает последнюю удачную позицию. Это нарушает чистоту инварианта, но необходимо для корректности.
Асимметричные действия: При нахождении target мы не выходим, а сдвигаем границу в сторону, противоположную от искомой границы. Для findFirst — влево, для findLast — вправо.
Выходное значение: В отсутствие элемента метод возвращает не -1, а точку вставки. Это делает API более универсальным для построения диапазонных запросов.
Поиск последнего вхождения: симметричная логика

Метод findLast зеркально отражает findFirst. Когда находится совпадение, мы продолжаем поиск в правой половине, потому что там может быть еще один дубликат. После завершения цикла left указывает на первый элемент строго больше target, поэтому left - 1 — это последний элемент, меньший или равный target.

#Java #для_новичков #beginner #algorithm #sorted #binary
👍3
Поиск диапазона

От границ к диапазону: композиция операций

Теперь, когда у нас есть инструменты для поиска границ, задача «найти все книги, изданные в 2020-2023 годах» решается композицией:

public class LibraryRangeSearch {

static class Book implements Comparable<Book> {
String title;
int year;

// Конструктор, геттеры...

@Override
public int compareTo(Book other) {
return Integer.compare(this.year, other.year);
}
}

/**
* Поиск всех книг в заданном диапазоне лет [startYear, endYear].
* Возвращает подмассив (views) для экономии памяти.
*/
public static List<Book> findBooksByYearRange(Book[] library, int startYear, int endYear) {
if (library == null || startYear > endYear) {
return Collections.emptyList();
}

// Создаем фиктивные книги-границы для поиска
Book startDummy = new Book("", startYear);
Book endDummy = new Book("", endYear);

// Находим первую книгу с year >= startYear
int leftIndex = findFirstIndex(library, startDummy);

// Находим последнюю книгу с year <= endYear
int rightIndex = findLastIndex(library, endDummy);

if (leftIndex == -1 || rightIndex == -1 || leftIndex > rightIndex) {
return Collections.emptyList();
}

// Возвращаем view для избежания копирования
return Arrays.asList(library).subList(leftIndex, rightIndex + 1);
}

// Адаптер для работы с Comparable объектами
private static int findFirstIndex(Book[] array, Book target) {
int left = 0, right = array.length - 1, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
int cmp = array[mid].compareTo(target);
if (cmp < 0) left = mid + 1;
else if (cmp > 0) right = mid - 1;
else { result = mid; right = mid - 1; }
}
return result != -1 ? result : left;
}

private static int findLastIndex(Book[] array, Book target) {
int left = 0, right = array.length - 1, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
int cmp = array[mid].compareTo(target);
if (cmp < 0) left = mid + 1;
else if (cmp > 0) right = mid - 1;
else { result = mid; left = mid + 1; }
}
return result != -1 ? result : left - 1;
}
}


Анализ производительности: Поиск диапазона требует двух бинарных поисков — O(log n) + O(log n) = O(log n). После этого мы получаем непосредственный доступ к результату за O(1). Если результатов много (k элементов), итоговая сложность O(log n + k). Это намного эффективнее линейного сканирования всей библиотеки O(n).

Важное замечание: Метод возвращает subList, который является view исходного массива. Изменения в исходном массиве отразятся в результате. Для защиты нужно скопировать: new ArrayList<>(Arrays.asList(...).subList(...)).


#Java #для_новичков #beginner #algorithm #sorted #binary
👍3
Альтернативный подход: поиск нижней границы + линейное сбирание

Если вы знаете, что диапазон редко содержит много элементов (например, книги за конкретный год в библиотеке с редкими изданиями), можно оптимизировать:
public static List<Book> findBooksByYearRangeOptimized(Book[] library, int startYear, int endYear) {
int startIdx = findFirstIndex(library, new Book("", startYear));
if (startIdx == -1 || startIdx >= library.length) return Collections.emptyList();

List<Book> result = new ArrayList<>();
for (int i = startIdx; i < library.length && library[i].year <= endYear; i++) {
result.add(library[i]);
}
return result;
}


Этот подход имеет сложность O(log n + k) в худшем случае, но с меньшими константами, потому что второй бинарный поиск заменен на последовательное чтение (кэш-приятно).


Интерполяционный поиск — когда данные говорят сами за себя

Интуиция: зачем всегда делить пополам?

Представьте телефонный справочник, где фамилии распределены равномерно по алфавиту. Ищете «Иванов». Классический бинарный поиск откроет справочник ровно посередине — на букве «М», затем на «Г», затем на «Д»... Вместо этого можно интерполировать: поскольку «И» находится примерно на 10% алфавита, сразу открыть страницу на 10% от общего объема.

Интерполяционный поиск заменяет слепое деление пополам на адресный расчет вероятной позиции на основе значений границ.

Математическая формула интерполяции

Если array[left] и array[right] известны, и target лежит между ними, то при равномерном распределении его позиция должна быть пропорциональна:
mid = left + (target - array[left]) * (right - left) / (array[right] - array[left])


Это формула линейной интерполяции. Она вычисляет, насколько далеко target находится от левой границы в долях от общего диапазона значений, и применяет этот же коэффициент к индексам.


#Java #для_новичков #beginner #algorithm #sorted #binary
👍2
Реализация и инварианты
public class InterpolationSearch {

/**
* Интерполяционный поиск для равномерно распределенных целочисленных данных.
* ВНИМАНИЕ: требует, чтобы array[left] < array[right] и данные были равномерными!
*/
public static int search(int[] array, int target) {
if (array == null) throw new IllegalArgumentException("Array cannot be null");

int left = 0;
int right = array.length - 1;

// Условие array[left] <= target <= array[right] критично для формулы
while (left <= right && target >= array[left] && target <= array[right]) {
// Если диапазон схлопнулся, переходим к линейному поиску
if (array[left] == array[right]) {
if (array[left] == target) return left;
break; // Не найден
}

// Интерполяция позиции
// Предотвращаем деление на ноль проверкой выше
int mid = left + (target - array[left]) * (right - left) / (array[right] - array[left]);

// Защита от выхода за границы (возможна при неравномерных данных)
mid = Math.max(left, Math.min(mid, right));

int midValue = array[mid];

if (midValue < target) {
left = mid + 1;
} else if (midValue > target) {
right = mid - 1;
} else {
return mid;
}
}

// Пост-проверка границ
if (left <= right && array[left] == target) return left;
return -1;
}
}


Критические условия применимости:

Равномерное распределение: Формула работает, если разность между соседними элементами примерно постоянна. Если данные сгущаются в некоторых зонах (например, 90% книг изданы в 2020-х, а остальные — растянуты на 50 лет), интерполяция будет постоянно ошибаться, откатываясь к бинарному поведению или хуже.
Отсутствие повторений на границах: Если array[left] == array[right], формула приводит к делению на ноль. В этом случае алгоритм должен деградировать к линейному поиску в этом поддиапазоне.
Целочисленное переполнение: Выражение (target - array[left]) * (right - left) может переполнить int при больших значениях. Для production-кода рекомендуется использовать long для промежуточных расчетов.


Анализ сложности и парадоксы

Средний случай: При равномерном распределении интерполяционный поиск достигает O(log log n). Это практически константа: для массива из 1 миллиарда элементов потребуется ~5 итераций. Это достигается за счет того, что каждый шаг не просто делит диапазон пополам, а приближается к цели экспоненциально быстро.

Худший случай: Если данные неравномерны (например, [1, 2, 3, 4, 5, 1000, 1001, 1002, 1003]), интерполяция может снова и снова попадать в «пустые» зоны, требуя O(n) сравнений. Это хуже бинарного поиска.

Практический вывод
: Интерполяционный поиск имеет смысл применять только тогда, когда вы точно знаете характер распределения данных и уверены в его равномерности. В остальных случаях бинарный поиск более надежен. В Java стандартная библиотека не содержит интерполяционного поиска из-за его узкой применимости и риска деградации.


#Java #для_новичков #beginner #algorithm #sorted #binary
👍3
Что выведет код?

public class Task260126 {
public static void main(String[] args) {
int[] arr = {2, 4, 6, 8, 10, 12, 14, 16, 18, 1000};
int index = interpolationSearch260126(arr, 2);
System.out.println(index);
}

static int interpolationSearch260126(int[] arr, int target) {
int low = 0;
int high = arr.length - 1;

while (low <= high && target >= arr[low] && target <= arr[high]) {
int pos = low + ((target - arr[low]) * (high - low)) / (arr[high] - arr[low]);

if (arr[pos] == target) return pos;
if (arr[pos] < target) low = pos + 1;
else high = pos - 1;
}
return -1;
}
}


#Tasks
👍2
Варианты ответа:
Anonymous Quiz
50%
0
21%
1
21%
-1
7%
Exception
👍3
Почему finalize() опасен даже если он “работает”? 🤓

Ответ:

finalize вызывается не сразу и не гарантирован вообще.

Объект может долго оставаться в памяти, даже если он логически мёртв. Ошибка в finalize может полностью блокировать сборку мусора для объекта. Кроме того, finalize выполняется в отдельном потоке GC, что усложняет отладку.

Именно поэтому finalize признан deprecated, а управление ресурсами должно выполняться явно через try-with-resources.



#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6
История IT-технологий сегодня — 27 января

ℹ️ Кто родился в этот день

Лью́ис Кэ́рролл (англ. Lewis Carroll, настоящее имя Чарльз Лютвидж До́джсон, или Чарльз Латуидж До́джсон (традиционная русская передача; сам Кэрролл произносил свою фамилию Додсон, ˈdɒdsən[11], ряд современных словарей даёт произношение ˈdɒdʒsən[12][13]), Charles Lutwidge Dodgson; 27 января 1832 — 14 января 1898) — математик и логик, работал над логическими диограммами, алгеброй логики и теорией вероятностей; его логические идеи и подход к формализации рассуждений — часть исторического фундамента логики, на которой стоит теоретическая информатика. Наиболее известные произведения — «Приключения Алисы в Стране чудес» и «Алиса в Зазеркалье», а также юмористическая поэма «Охота на Снарка».


🌐 Знаковые события

1994 — Джим Кларк уходит из Silicon Graphics и создаёт Mosaic Communications (позже Netscape): компания стала одной из первых, кто коммерциализировал веб‑браузер, что сильно ускорило развитие и распространение интернета и веб‑технологий. Доля пользователей Netscape достигла пика в середине 1990-х годов, но к концу 2006 года упала до менее чем одного процента.


#Biography #Birth_Date #Events #27Января
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Глава 4: Эффективный поиск. Бинарный и не только

Практика

Сегодня мы применим знания о эффективном поиске на проекте «Библиотека».

Мы отсортируем список книг по названию, реализуем бинарный поиск для нахождения первой книги заданного автора (в отсортированном списке), и проведём сравнение времени выполнения линейного и бинарного поиска на коллекциях разных размеров. Это поможет наглядно увидеть преимущества логарифмического поиска O(log n) над линейным O(n), понять предпосылки (сортировка данных) и границы применимости (когда сортировка окупается).

Подготовка к уроку


Перед началом убедитесь, что проект готов, и вспомните ключевые концепции:
Бинарный поиск работает только на отсортированных данных, делит интервал пополам.
Линейный поиск — перебор O(n), всегда работает.
Сортировка O(n log n) — предпосылка для бинарного.

Откройте проект «Библиотека»: Убедитесь, что List<Book> books содержит достаточно книг (добавьте метод для генерации тестовых данных).
Импортируйте пакеты: java.util.Arrays (для бинарного поиска), java.util.Random (для генерации данных).
Генерация больших данных: Создайте метод generateBooks(int size) для создания списков размером 100, 10 000, 1 000 000 (используйте Random для title/author/year).


Отсортировать книги по названию

Обновите Book для Comparable (если не сделано): Реализуйте compareTo по title (this.title.compareTo(other.title)).
Создайте метод sortByTitle(): Используйте Collections.sort(books) — сортирует по Comparable (названию).
Вывод: После сортировки вызовите printAllBooks() для проверки.


Реализовать бинарный поиск для нахождения первой книги заданного автора

Отсортируйте по автору: Создайте Comparator<Book> byAuthor = Comparator.comparing(Book::getAuthor);, затем books.sort(byAuthor).

Реализуйте метод findFirstBookByAuthor(String author):
Используйте Arrays.binarySearch, но поскольку books — List, преобразуйте в массив или реализуйте вручную.

Вручную:
int low = 0, high = books.size() - 1; 

while (low <= high) {
mid = (low + high) / 2; cmp = books.get(mid).getAuthor().compareTo(author);

if (cmp < 0) low = mid + 1;
else if (cmp > 0) high = mid - 1;
else { // Найден, найти первый:
while (mid > 0 && books.get(mid-1).getAuthor().equals(author))
mid--;
return books.get(mid);
}
}


Если не найден — return null.

Проверка: После сортировки вызовите метод, выведите найденную книгу.


Сравнить время линейного и бинарного поиска на разных размерах

Реализуйте линейный поиск: Метод linearSearchByAuthor(String author) — for-each, если совпадение — return book.
Измерение времени: Используйте System.nanoTime() before/after.

Тест на размерах:
Для 100: generateBooks(100), sortByAuthor, time linear vs binary (поиск рандомного автора).
Для 10 000 и 1 000 000: Аналогично, усредните по 100 запускам.
Выводите: "Для n=[size]: Линейный: [time ns], Бинарный: [time ns]".

Анализ: Точка окупаемости сортировки + бинарный поиск vs множественный линейный поиск
Точка окупаемости — момент, когда стоимость сортировки + m бинарных поисков становится меньше m линейных поисков.

Расчёт:
Линейный: O(n) per search → m * n
Бинарный: O(n log n) sort + m * log n
Окупаемость: n log n + m log n < m n → m > (n log n) / (n - log n) ≈ log n (для больших n)
Пример: n = 1000, log n ≈ 10 — окупаемость после ~10 поисков.
n = 1 млн, log n ≈ 20 — после ~20 поисков.
Граница: Для малого m или n — линейный дешевле (нет сортировки).
В библиотеке: Если поиски редки — линейный; если часты — sort + binary.

#Java #для_новичков #beginner #algorithm #sorted #binary #практика
👍4