Files
aston-team-project/docs/tasks/StudentOccurrenceIndexFinder.md
T

689 lines
16 KiB
Markdown
Raw Normal View History

# Техническое задание: многопоточный поиск индексов `Student`
## Задача
Реализовать многопоточный поиск всех вхождений заданного объекта `Student` в `MyList<Student>`.
Метод **не должен выводить никаких сообщений в консоль**.
Результатом работы должен быть массив `int[]`, содержащий индексы всех элементов исходной коллекции, которые равны переданному `Student`.
Сравнение выполнять через существующий `Student.equals()`.
---
## Входная информация
Основной метод принимает:
```java
MyList<Student> students
```
Исходная коллекция студентов.
```java
Student target
```
Студент, вхождения которого необходимо найти.
---
## Выходная информация
Метод возвращает:
```java
int[]
```
Массив должен содержать индексы всех совпавших элементов.
Пример:
```text
Исходная коллекция:
[Student A, Student B, Student A, Student C, Student A]
Результат:
[0, 2, 4]
```
Индексы должны быть расположены в порядке возрастания.
При отсутствии совпадений:
```text
[]
```
При пустой коллекции:
```text
[]
```
---
# Название класса
```text
StudentOccurrenceIndexFinder
```
---
# Публичные методы и контракты
## `findOccurrences`
```java
public int[] findOccurrences(
MyList<Student> students,
Student target
);
```
Метод должен:
1. Проверить входные параметры.
2. Разделить коллекцию на диапазоны индексов.
3. Передать каждый диапазон отдельной задаче.
4. Выполнить поиск в нескольких потоках.
5. Получить от каждого потока найденные индексы.
6. Объединить результаты.
7. Вернуть индексы в порядке возрастания.
8. Не изменять исходную коллекцию.
9. Не изменять объекты `Student`.
10. Не выводить ничего в консоль.
---
# Вспомогательные методы
Количество и вид `private` методов могут быть выбраны разработчиком.
Рекомендуется выделить:
```java
private int calculateThreadCount(int size);
```
Определяет количество рабочих потоков.
```java
private int countChunkSize(int size, int threadCount);
```
Определяет размер диапазона для одной задачи.
```java
private MyList<Integer> findInRange(
MyList<Student> students,
Student target,
int fromIndex,
int toIndex
);
```
Обрабатывает диапазон:
```text
[fromIndex, toIndex)
```
и возвращает индексы найденных совпадений.
---
# Работа с многопоточностью
## Рекомендуемый подход
Не использовать общий контейнер результатов, к которому одновременно обращаются несколько потоков.
Также **не рекомендуется использовать мьютексы** (`synchronized`, `Lock`, `ReentrantLock`) для хранения результата.
Лучше использовать модель:
```text
MyList<Student>
│
┌────────────┼────────────┐
↓ ↓ ↓
Поток 1 Поток 2 Поток 3
диапазон диапазон диапазон
│ │ │
↓ ↓ ↓
MyList<Integer> MyList<Integer> MyList<Integer>
└────────────┼────────────┘
↓
основной поток
↓
merge
↓
int[]
```
Каждый поток создаёт **собственный контейнер `MyList<Integer>`** и записывает найденные индексы только в него.
Это позволяет избежать состояния гонки без синхронизации общей структуры.
---
# Разбиение коллекции на потоки
Количество потоков рекомендуется определять на основании:
```java
Runtime.getRuntime().availableProcessors()
```
Количество потоков не должно превышать количество элементов:
```text
threadCount = min(
availableProcessors(),
students.size()
)
```
### Пример
Для коллекции из `10000` элементов и четырёх рабочих потоков:
```text
Поток 1 → [0, 2500)
Поток 2 → [2500, 5000)
Поток 3 → [5000, 7500)
Поток 4 → [7500, 10000)
```
Каждый индекс должен принадлежать **ровно одному диапазону**.
Не допускаются:
- пропущенные индексы;
- обработка одного индекса несколькими потоками.
---
# Поиск внутри потока
Каждая задача обрабатывает только свой диапазон.
Для каждого индекса:
```java
Student student = students.get(index);
```
затем:
```java
target.equals(student)
```
Если результат `true`, индекс добавляется в локальный:
```java
MyList<Integer>
```
Пример:
```text
Поток 1 → [0, 8, 15]
Поток 2 → [21, 24]
Поток 3 → [30, 42, 48]
Поток 4 → [63]
```
---
# Объединение результатов
Основной поток после завершения всех задач должен получить локальные результаты и объединить их.
Рекомендуется передавать задачи в порядке возрастания диапазонов:
```text
Результат 1 → диапазон 0
Результат 2 → диапазон 1
Результат 3 → диапазон 2
...
```
Если каждая задача добавляет индексы в возрастающем порядке, а результаты объединяются в порядке диапазонов, итоговый массив уже будет отсортирован:
```text
[0, 8, 15, 21, 24, 30, 42, 48, 63]
```
В этом случае дополнительная сортировка результата не требуется.
**`Arrays.sort()` и другие готовые методы сортировки использовать не следует**, поскольку порядок можно гарантировать самим алгоритмом объединения.
---
# Потокобезопасность
## Мьютексы
Использование мьютексов для результата **не требуется**.
Не рекомендуется использовать:
```java
synchronized
```
```java
Lock
```
```java
ReentrantLock
```
```java
AtomicInteger
```
для общего результата.
Причина: потоки не должны изменять одну общую структуру.
Каждый поток работает со своим:
```text
MyList<Integer>
```
а объединение выполняется после завершения параллельной части одним потоком.
---
# Рекомендуемые классы и импорты
Для реализации рекомендуется использовать стандартные средства Java:
```java
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
```
Рекомендуемая схема:
```text
ExecutorService
↓
Callable<MyList<Integer>>
↓
Future<MyList<Integer>>
```
Каждый `Callable` обрабатывает свой диапазон и возвращает собственный `MyList<Integer>`.
После получения всех `Future` основной метод объединяет результаты.
После завершения работы `ExecutorService` должен быть корректно закрыт.
При `InterruptedException` необходимо восстановить статус прерывания текущего потока:
```java
Thread.currentThread().interrupt();
```
---
# Использование `MyList`
Сторонние контейнеры **не допускаются**.
Запрещено использовать:
```text
ArrayList
LinkedList
Vector
CopyOnWriteArrayList
ConcurrentLinkedQueue
и другие стандартные контейнеры
```
как хранилище промежуточных или итоговых результатов.
Для результатов каждого потока использовать:
```java
MyList<Integer>
```
Итог преобразовать в:
```java
int[]
```
---
# Требуется ли изменять `MyList`
Текущего интерфейса **достаточно для реализации задачи**:
```java
public interface MyList<T> {
void add(T element);
T get(int index);
T set(int index, T element);
T remove(int index);
int size();
boolean isEmpty();
}
```
Дополнительные методы добавлять **не требуется**.
Для задачи достаточно:
```java
size()
get(index)
add(index)
```
Поскольку поиск выполняется только на чтение, а результаты каждого потока добавляются в его собственный `MyList<Integer>`, существующего интерфейса достаточно.
### Важно
Метод:
```java
void add(T element);
```
должен добавлять элемент в конец коллекции.
Это позволяет каждому потоку формировать свои индексы в порядке их обнаружения.
---
# Использование `Student`
Изменения в `Student` **не требуются**.
Использовать существующий метод:
```java
equals(Object obj)
```
Сравнение выполнять:
```java
target.equals(students.get(index))
```
Не использовать:
```text
==
```
Не сравнивать объекты вручную по отдельным полям.
Не использовать `toString()` для определения совпадения.
---
# `StudentBuilder`
Изменения в `StudentBuilder` **не требуются**.
Он не участвует в алгоритме поиска.
---
# Обработка входных данных
Если:
```java
students == null
```
выбросить:
```java
IllegalArgumentException
```
Если:
```java
target == null
```
выбросить:
```java
IllegalArgumentException
```
Для пустой коллекции вернуть:
```java
new int[0]
```
Без создания рабочих потоков.
---
# Обобщённый алгоритм реализации
```text
1. Проверить students и target.
2. Получить размер коллекции.
3. Если размер равен 0 → вернуть [].
4. Вычислить количество потоков.
5. Разделить индексы коллекции на непересекающиеся диапазоны.
6. Создать Callable для каждого диапазона.
7. Запустить задачи через ExecutorService.
8. Каждый поток:
- получает fromIndex и toIndex;
- проходит свой диапазон;
- сравнивает Student через equals();
- записывает найденные индексы в собственный MyList<Integer>.
9. Основной поток получает результаты через Future.
10. Объединяет MyList<Integer> в порядке диапазонов.
11. Преобразует результат в int[].
12. Возвращает массив.
```
---
# Примеры тестов
## Одно совпадение
```text
[A, B, C, D]
```
Искомый:
```text
C
```
Ожидается:
```text
[2]
```
## Несколько совпадений
```text
[A, B, A, C, A]
```
Ожидается:
```text
[0, 2, 4]
```
## Нет совпадений
```text
[A, B, C]
```
Искомый:
```text
D
```
Ожидается:
```text
[]
```
## Все элементы совпадают
```text
[A, A, A, A]
```
Ожидается:
```text
[0, 1, 2, 3]
```
## Пустая коллекция
Ожидается:
```text
[]
```
## Один элемент
Проверить:
```text
[A] + A → [0]
[A] + B → []
```
## Проверка границ диапазонов потоков
Проверить коллекции размеров:
```text
1
2
3
4
10
100
1000
```
Особое внимание уделить совпадениям:
```text
на последнем элементе одного диапазона;
на первом элементе следующего диапазона.
```
Каждый индекс должен быть найден ровно один раз.
## Проверка порядка результата
Создать совпадения в разных диапазонах.
Проверить, что результат имеет вид:
```text
[0, 3, 7, 12, 25, 49, 100]
```
и находится в порядке возрастания без использования дополнительной сортировки.
## Проверка `Student.equals()`
Создать два разных экземпляра `Student` с одинаковыми значениями полей.
Один поместить в коллекцию, второй передать как `target`.
Проверить, что индекс считается совпадением согласно `equals()`.
## Проверка неизменности коллекции
После выполнения метода проверить:
- размер `MyList`;
- порядок элементов;
- значения всех элементов.
## Проверка отсутствия вывода
Перехватить стандартный вывод и убедиться, что выполнение `findOccurrences()` не выводит сообщений в консоль.
## Большая коллекция
Создать коллекцию, например из `100000` студентов, с заранее известными индексами совпадений.
Проверить:
- все ожидаемые индексы найдены;
- лишних индексов нет;
- индексы идут по возрастанию;
- итоговый массив имеет правильный размер.
## Git
Название ветки:
```text
feature/student-occurrence-index-finder
```
## Результат
Необходимо реализовать:
```text
StudentOccurrenceIndexFinder.java
```
Существующие интерфейсы и классы:
```text
MyList.java
Student.java
StudentBuilder.java
```
изменять не требуется.