Merge branch 'develop' into feature/ui
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
package ru.test.maven.spring;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
public class MyCollectors
|
||||||
|
{
|
||||||
|
private MyCollectors() {
|
||||||
|
}
|
||||||
|
public static <T> Collector<T, MyList<T>, MyList<T>> toMyList() {
|
||||||
|
return new MyListCollector<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package ru.test.maven.spring;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BinaryOperator;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
public class MyListCollector <T> implements Collector<T, MyList<T>, MyList<T>>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Supplier<MyList<T>> supplier()
|
||||||
|
{
|
||||||
|
return () -> new MyLinkedList<>();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public BiConsumer<MyList<T>, T> accumulator() {
|
||||||
|
|
||||||
|
return (list, element) -> list.add(element);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public BinaryOperator<MyList<T>> combiner() {
|
||||||
|
return (list1, list2) -> {
|
||||||
|
for (int i = 0; i < list2.size(); i++) {
|
||||||
|
list1.add(list2.get(i));
|
||||||
|
}
|
||||||
|
return list1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Function<MyList<T>, MyList<T>> finisher()
|
||||||
|
{
|
||||||
|
return list -> list;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Set<Collector.Characteristics> characteristics() {
|
||||||
|
return java.util.Set.of(
|
||||||
|
Collector.Characteristics.IDENTITY_FINISH
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package ru.project.collectors;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
|
||||||
|
public class MyCollectors
|
||||||
|
{
|
||||||
|
private MyCollectors() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Collector<T, MyList<T>, MyList<T>> toMyList()
|
||||||
|
{
|
||||||
|
return new MyListCollector<>();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package ru.project.collectors;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BinaryOperator;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
public class MyListCollector <T> implements Collector<T, MyList<T>, MyList<T>>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Supplier<MyList<T>> supplier()
|
||||||
|
{
|
||||||
|
return () -> new MyLinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiConsumer<MyList<T>, T> accumulator() {
|
||||||
|
|
||||||
|
return (list, element) -> list.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BinaryOperator<MyList<T>> combiner() {
|
||||||
|
return (list1, list2) -> {
|
||||||
|
list1.addAll(list2);
|
||||||
|
return list1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Function<MyList<T>, MyList<T>> finisher()
|
||||||
|
{
|
||||||
|
return list -> list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<Collector.Characteristics> characteristics() {
|
||||||
|
return java.util.Set.of(
|
||||||
|
Collector.Characteristics.IDENTITY_FINISH
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package ru.project.finder;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
public class StudentOccurrenceIndexFinder
|
||||||
|
{
|
||||||
|
public int[] findOccurrences(MyList<Student> students, Student target) {
|
||||||
|
if (students == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (target == null) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
if (students.isEmpty()) {
|
||||||
|
return new int[0];
|
||||||
|
}
|
||||||
|
int size = students.size();
|
||||||
|
int threadCount = calculateThreadCount(size);
|
||||||
|
int chunkSize = countChunkSize(size, threadCount);
|
||||||
|
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
||||||
|
MyList<Future<MyList<Integer>>> futures = new MyLinkedList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
int fromIndex = i * chunkSize;
|
||||||
|
int toIndex = Math.min(fromIndex + chunkSize, size);
|
||||||
|
|
||||||
|
if (fromIndex >= toIndex) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int start = fromIndex;
|
||||||
|
final int end = toIndex;
|
||||||
|
|
||||||
|
Future<MyList<Integer>> future = executor.submit(() -> {
|
||||||
|
return findInRange(students, target, start, end);
|
||||||
|
});
|
||||||
|
futures.add(future);
|
||||||
|
}
|
||||||
|
MyList<Integer> allIndices = new MyLinkedList<>();
|
||||||
|
try {
|
||||||
|
for (int i = 0; i < futures.size(); i++) {
|
||||||
|
Future<MyList<Integer>> future = futures.get(i);
|
||||||
|
MyList<Integer> partialResult = future.get();
|
||||||
|
|
||||||
|
if (partialResult != null) {
|
||||||
|
for (int j = 0; j < partialResult.size(); j++) {
|
||||||
|
allIndices.add(partialResult.get(j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Ошибка при поиске", e);
|
||||||
|
} finally {
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
sortList(allIndices);
|
||||||
|
|
||||||
|
int[] resultArray = new int[allIndices.size()];
|
||||||
|
for (int i = 0; i < allIndices.size(); i++) {
|
||||||
|
resultArray[i] = allIndices.get(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortList(MyList<Integer> list) {
|
||||||
|
if (list.size() <= 1) return;
|
||||||
|
|
||||||
|
MyList<Integer> sorted = new MyLinkedList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
int value = list.get(i);
|
||||||
|
int insertIndex = 0;
|
||||||
|
while (insertIndex < sorted.size()) {
|
||||||
|
if (sorted.get(insertIndex) > value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
insertIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
MyList<Integer> temp = new MyLinkedList<>();
|
||||||
|
for (int k = 0; k < insertIndex; k++) {
|
||||||
|
temp.add(sorted.get(k));
|
||||||
|
}
|
||||||
|
temp.add(value);
|
||||||
|
for (int k = insertIndex; k < sorted.size(); k++) {
|
||||||
|
temp.add(sorted.get(k));
|
||||||
|
}
|
||||||
|
sorted = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!list.isEmpty()) {
|
||||||
|
list.remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < sorted.size(); i++) {
|
||||||
|
list.add(sorted.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculateThreadCount(int size) {
|
||||||
|
int processors = Runtime.getRuntime().availableProcessors();
|
||||||
|
return Math.min(processors, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countChunkSize(int size, int threadCount) {
|
||||||
|
if (threadCount == 0) return 0;
|
||||||
|
return (size + threadCount - 1) / threadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MyList<Integer> findInRange(MyList<Student> students, Student target, int fromIndex, int toIndex) {
|
||||||
|
MyList<Integer> foundIndices = new MyLinkedList<>();
|
||||||
|
|
||||||
|
for (int i = fromIndex; i < toIndex; i++) {
|
||||||
|
Student current = students.get(i);
|
||||||
|
if (current != null && current.equals(target)) {
|
||||||
|
foundIndices.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundIndices;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
import ru.project.student.Student;
|
||||||
|
|
||||||
|
public class AverageGradeRangeCriteria implements StudentSearchCriteria {
|
||||||
|
private final double min;
|
||||||
|
private final double max;
|
||||||
|
|
||||||
|
public AverageGradeRangeCriteria(double min, double max) {
|
||||||
|
if (min > max) {
|
||||||
|
throw new IllegalArgumentException("Min grade must be <= max grade");
|
||||||
|
}
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Student student) {
|
||||||
|
double grade = student.getAverageGrade();
|
||||||
|
return grade >= min && grade <= max;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
import ru.project.student.Student;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class CompositeStudentSearchCriteria implements StudentSearchCriteria {
|
||||||
|
private final SearchOperation operation;
|
||||||
|
private final List<StudentSearchCriteria> criteriaList = new ArrayList<>();
|
||||||
|
|
||||||
|
public CompositeStudentSearchCriteria(SearchOperation operation) {
|
||||||
|
if (operation == null) {
|
||||||
|
throw new IllegalArgumentException("Operation must not be null");
|
||||||
|
}
|
||||||
|
this.operation = operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(StudentSearchCriteria criteria) {
|
||||||
|
if (criteria == null) {
|
||||||
|
throw new IllegalArgumentException("Criteria must not be null");
|
||||||
|
}
|
||||||
|
criteriaList.add(criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(StudentSearchCriteria criteria) {
|
||||||
|
criteriaList.remove(criteria);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Student student) {
|
||||||
|
if (criteriaList.isEmpty()) {
|
||||||
|
return operation == SearchOperation.INTERSECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation == SearchOperation.UNION) {
|
||||||
|
for (StudentSearchCriteria c : criteriaList) {
|
||||||
|
if (c.matches(student)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
for (StudentSearchCriteria c : criteriaList) {
|
||||||
|
if (!c.matches(student)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
import ru.project.student.Student;
|
||||||
|
|
||||||
|
public class GroupNumberRangeCriteria implements StudentSearchCriteria {
|
||||||
|
private final String min;
|
||||||
|
private final String max;
|
||||||
|
|
||||||
|
public GroupNumberRangeCriteria(String min, String max) {
|
||||||
|
if (min == null || max == null) {
|
||||||
|
throw new IllegalArgumentException("Min and max must not be null");
|
||||||
|
}
|
||||||
|
if (min.compareTo(max) > 0) {
|
||||||
|
throw new IllegalArgumentException("Min group must be <= max group");
|
||||||
|
}
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Student student) {
|
||||||
|
String group = student.getGroupNumber();
|
||||||
|
return group.compareTo(min) >= 0 && group.compareTo(max) <= 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
import ru.project.student.Student;
|
||||||
|
|
||||||
|
public class RecordBookNumberRangeCriteria implements StudentSearchCriteria {
|
||||||
|
private final int min;
|
||||||
|
private final int max;
|
||||||
|
|
||||||
|
public RecordBookNumberRangeCriteria(int min, int max) {
|
||||||
|
if (min > max) {
|
||||||
|
throw new IllegalArgumentException("Min record number must be <= max");
|
||||||
|
}
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Student student) {
|
||||||
|
int number = student.getRecordBookNumber();
|
||||||
|
return number >= min && number <= max;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
public enum SearchOperation {
|
||||||
|
UNION,
|
||||||
|
INTERSECTION
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
import ru.project.student.Student;
|
||||||
|
|
||||||
|
public interface StudentSearchCriteria {
|
||||||
|
boolean matches(Student student);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
import ru.project.list.MyList;
|
||||||
|
import ru.project.list.MyLinkedList;
|
||||||
|
import ru.project.student.Student;
|
||||||
|
|
||||||
|
public class StudentSearchService {
|
||||||
|
private final StudentSearchCriteria criteria;
|
||||||
|
|
||||||
|
public StudentSearchService(StudentSearchCriteria criteria) {
|
||||||
|
if (criteria == null) {
|
||||||
|
throw new IllegalArgumentException("Criteria must not be null");
|
||||||
|
}
|
||||||
|
this.criteria = criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyList<Student> find(MyList<Student> students) {
|
||||||
|
if (students == null) {
|
||||||
|
return new MyLinkedList<>();
|
||||||
|
}
|
||||||
|
MyList<Student> result = new MyLinkedList<>();
|
||||||
|
for (int i = 0; i < students.size(); i++) {
|
||||||
|
Student s = students.get(i);
|
||||||
|
if (criteria.matches(s)) {
|
||||||
|
result.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package ru.test.maven.spring;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
public class MyListCollectorTest
|
||||||
|
{
|
||||||
|
private final MyListCollector<Integer> collector = new MyListCollector<>();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestCollectInOrderSequential() {
|
||||||
|
MyList<Integer> result = IntStream.range(0, 10)
|
||||||
|
.boxed()
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertEquals(10, result.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
assertEquals(i, (int) result.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestCollectInOrderParallel() {
|
||||||
|
MyList<Integer> result = IntStream.range(0, 100)
|
||||||
|
.parallel()
|
||||||
|
.boxed()
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertEquals(100, result.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
assertEquals(i, (int) result.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestHandleEmptyStream() {
|
||||||
|
MyList<Integer> result = IntStream.empty()
|
||||||
|
.boxed()
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertTrue(result.isEmpty());
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTestNotUseStandardList() {
|
||||||
|
MyList<String> result = IntStream.range(0, 5)
|
||||||
|
.mapToObj(i -> "item" + i)
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertInstanceOf(MyList.class, result);
|
||||||
|
assertFalse(result instanceof java.util.List);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTestHaveCorrectCharacteristics() {
|
||||||
|
Set<Collector.Characteristics> characteristics = collector.characteristics();
|
||||||
|
assertTrue(characteristics.contains(java.util.stream.Collector.Characteristics.IDENTITY_FINISH));
|
||||||
|
assertFalse(characteristics.contains(java.util.stream.Collector.Characteristics.UNORDERED));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package ru.test.maven.spring;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collector;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class MyListCollectorTest
|
||||||
|
{
|
||||||
|
private final MyListCollector<Integer> collector = new MyListCollector<>();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestCollectInOrderSequential() {
|
||||||
|
MyList<Integer> result = IntStream.range(0, 10)
|
||||||
|
.boxed()
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertEquals(10, result.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
assertEquals(i, (int) result.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestCollectInOrderParallel() {
|
||||||
|
MyList<Integer> result = IntStream.range(0, 100)
|
||||||
|
.parallel()
|
||||||
|
.boxed()
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertEquals(100, result.size());
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
assertEquals(i, (int) result.get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTestHandleEmptyStream() {
|
||||||
|
MyList<Integer> result = IntStream.empty()
|
||||||
|
.boxed()
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertTrue(result.isEmpty());
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTestNotUseStandardList() {
|
||||||
|
MyList<String> result = IntStream.range(0, 5)
|
||||||
|
.mapToObj(i -> "item" + i)
|
||||||
|
.collect(MyCollectors.toMyList());
|
||||||
|
|
||||||
|
assertInstanceOf(MyList.class, result);
|
||||||
|
assertFalse(result instanceof java.util.List);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTestHaveCorrectCharacteristics() {
|
||||||
|
Set<Collector.Characteristics> characteristics = collector.characteristics();
|
||||||
|
assertTrue(characteristics.contains(java.util.stream.Collector.Characteristics.IDENTITY_FINISH));
|
||||||
|
assertFalse(characteristics.contains(java.util.stream.Collector.Characteristics.UNORDERED));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package ru.project.finder;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class StudentOccurrenceIndexFinderTest
|
||||||
|
{
|
||||||
|
private StudentOccurrenceIndexFinder finder;
|
||||||
|
private MyList<Student> students;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
finder = new StudentOccurrenceIndexFinder();
|
||||||
|
students = new MyLinkedList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_emptyList_returnsEmptyArray() {
|
||||||
|
int[] result = finder.findOccurrences(students, new Student("A12", 4.5, 1));
|
||||||
|
assertEquals(0, result.length);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_noMatches_returnsEmptyArray() {
|
||||||
|
students.add(new Student("A12", 4.0, 101));
|
||||||
|
students.add(new Student("B23", 3.5, 102));
|
||||||
|
Student target = new Student("C34", 4.8, 103); // такого нет
|
||||||
|
|
||||||
|
int[] result = finder.findOccurrences(students, target);
|
||||||
|
assertEquals(0, result.length);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_singleMatch_returnsCorrectIndex() {
|
||||||
|
students.add(new Student("A12", 4.0, 101));
|
||||||
|
students.add(new Student("B23", 3.5, 102)); // target здесь
|
||||||
|
students.add(new Student("C34", 4.8, 103));
|
||||||
|
|
||||||
|
Student target = new Student("B23", 3.5, 102);
|
||||||
|
int[] result = finder.findOccurrences(students, target);
|
||||||
|
|
||||||
|
assertEquals(1, result.length);
|
||||||
|
assertEquals(1, result[0]);
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_multipleMatches_returnsAllIndicesSorted() {
|
||||||
|
// индексы: 0, 2, 4 — должны вернуться в порядке возрастания
|
||||||
|
students.add(new Student("A12", 4.5, 101)); // match
|
||||||
|
students.add(new Student("B23", 3.8, 102));
|
||||||
|
students.add(new Student("A12", 4.5, 101)); // match
|
||||||
|
students.add(new Student("C34", 4.0, 103));
|
||||||
|
students.add(new Student("A12", 4.5, 101)); // match
|
||||||
|
|
||||||
|
Student target = new Student("A12", 4.5, 101);
|
||||||
|
int[] result = finder.findOccurrences(students, target);
|
||||||
|
|
||||||
|
assertEquals(3, result.length);
|
||||||
|
assertArrayEquals(new int[]{0, 2, 4}, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_allElementsMatch_returnsAllIndices() {
|
||||||
|
Student s = new Student("X99", 5.0, 999);
|
||||||
|
students.add(s);
|
||||||
|
students.add(s);
|
||||||
|
students.add(s);
|
||||||
|
|
||||||
|
int[] result = finder.findOccurrences(students, s);
|
||||||
|
assertEquals(3, result.length);
|
||||||
|
assertArrayEquals(new int[]{0, 1, 2}, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_differentAverageGrade_noMatch() {
|
||||||
|
students.add(new Student("A12", 4.50, 101));
|
||||||
|
Student target = new Student("A12", 4.51, 101); // отличается балл
|
||||||
|
|
||||||
|
int[] result = finder.findOccurrences(students, target);
|
||||||
|
assertEquals(0, result.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_differentGroupNumber_noMatch() {
|
||||||
|
students.add(new Student("A12", 4.5, 101));
|
||||||
|
Student target = new Student("B12", 4.5, 101); // другая группа
|
||||||
|
|
||||||
|
int[] result = finder.findOccurrences(students, target);
|
||||||
|
assertEquals(0, result.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_differentRecordBookNumber_noMatch() {
|
||||||
|
students.add(new Student("A12", 4.5, 101));
|
||||||
|
Student target = new Student("A12", 4.5, 102); // другой номер зачетки
|
||||||
|
|
||||||
|
int[] result = finder.findOccurrences(students, target);
|
||||||
|
assertEquals(0, result.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_nullList_throwsException() {
|
||||||
|
Student target = new Student("A12", 4.5, 101);
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> finder.findOccurrences(null, target));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_nullTarget_throwsException() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> finder.findOccurrences(students, null));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void shouldTesfindOccurrences_resultIsSorted() {
|
||||||
|
String[] validGroups = {"A00", "A01", "B12", "C99", "X55"};
|
||||||
|
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
String group = validGroups[i % validGroups.length];
|
||||||
|
double grade = (3.0 + (i % 3)) / 2.0;
|
||||||
|
if (i % 3 == 0) {
|
||||||
|
|
||||||
|
students.add(new Student("A12", 4.5, 101));
|
||||||
|
} else {
|
||||||
|
students.add(new Student(group, grade, 200 + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Student target = new Student("A12", 4.5, 101);
|
||||||
|
int[] result = finder.findOccurrences(students, target);
|
||||||
|
|
||||||
|
|
||||||
|
for (int i = 1; i < result.length; i++) {
|
||||||
|
assertTrue(result[i] > result[i - 1], "Результат должен быть отсортирован");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
package ru.project.search;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import ru.project.list.MyList;
|
||||||
|
import ru.project.list.MyLinkedList;
|
||||||
|
import ru.project.student.Student;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class StudentSearchTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGroupNumberRangeMatchesInside() {
|
||||||
|
StudentSearchCriteria criteria = new GroupNumberRangeCriteria("A10", "C30");
|
||||||
|
Student student = new Student("B20", 4.5, 123);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGroupNumberRangeMatchesLowerBoundary() {
|
||||||
|
StudentSearchCriteria criteria = new GroupNumberRangeCriteria("A10", "C30");
|
||||||
|
Student student = new Student("A10", 4.5, 123);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGroupNumberRangeMatchesUpperBoundary() {
|
||||||
|
StudentSearchCriteria criteria = new GroupNumberRangeCriteria("A10", "C30");
|
||||||
|
Student student = new Student("C30", 4.5, 123);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGroupNumberRangeDoesNotMatchOutside() {
|
||||||
|
StudentSearchCriteria criteria = new GroupNumberRangeCriteria("A10", "C30");
|
||||||
|
Student student = new Student("D00", 4.5, 123);
|
||||||
|
assertFalse(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGroupNumberRangeInvalidMinMaxThrowsException() {
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> new GroupNumberRangeCriteria("C30", "A10"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testGroupNumberRangeNullValuesThrowsException() {
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> new GroupNumberRangeCriteria(null, "A10"));
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> new GroupNumberRangeCriteria("A10", null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAverageGradeRangeMatchesInside() {
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
Student student = new Student("A12", 4.5, 123);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAverageGradeRangeMatchesLowerBoundary() {
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
Student student = new Student("A12", 4.0, 123);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAverageGradeRangeMatchesUpperBoundary() {
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
Student student = new Student("A12", 5.0, 123);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAverageGradeRangeDoesNotMatchBelow() {
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
Student student = new Student("A12", 3.9, 123);
|
||||||
|
assertFalse(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAverageGradeRangeDoesNotMatchAbove() {
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
Student student = new Student("A12", 5.1, 123);
|
||||||
|
assertFalse(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testAverageGradeRangeInvalidMinMaxThrowsException() {
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> new AverageGradeRangeCriteria(5.0, 4.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRecordBookNumberRangeMatchesInside() {
|
||||||
|
StudentSearchCriteria criteria = new RecordBookNumberRangeCriteria(100, 200);
|
||||||
|
Student student = new Student("A12", 4.5, 150);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRecordBookNumberRangeMatchesLowerBoundary() {
|
||||||
|
StudentSearchCriteria criteria = new RecordBookNumberRangeCriteria(100, 200);
|
||||||
|
Student student = new Student("A12", 4.5, 100);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRecordBookNumberRangeMatchesUpperBoundary() {
|
||||||
|
StudentSearchCriteria criteria = new RecordBookNumberRangeCriteria(100, 200);
|
||||||
|
Student student = new Student("A12", 4.5, 200);
|
||||||
|
assertTrue(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRecordBookNumberRangeDoesNotMatchBelow() {
|
||||||
|
StudentSearchCriteria criteria = new RecordBookNumberRangeCriteria(100, 200);
|
||||||
|
Student student = new Student("A12", 4.5, 99);
|
||||||
|
assertFalse(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRecordBookNumberRangeDoesNotMatchAbove() {
|
||||||
|
StudentSearchCriteria criteria = new RecordBookNumberRangeCriteria(100, 200);
|
||||||
|
Student student = new Student("A12", 4.5, 201);
|
||||||
|
assertFalse(criteria.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRecordBookNumberRangeInvalidMinMaxThrowsException() {
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> new RecordBookNumberRangeCriteria(200, 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompositeIntersectionEmptyReturnsTrue() {
|
||||||
|
CompositeStudentSearchCriteria composite = new CompositeStudentSearchCriteria(SearchOperation.INTERSECTION);
|
||||||
|
Student student = new Student("A12", 4.5, 123);
|
||||||
|
assertTrue(composite.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompositeUnionEmptyReturnsFalse() {
|
||||||
|
CompositeStudentSearchCriteria composite = new CompositeStudentSearchCriteria(SearchOperation.UNION);
|
||||||
|
Student student = new Student("A12", 4.5, 123);
|
||||||
|
assertFalse(composite.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompositeIntersectionAllTrue() {
|
||||||
|
CompositeStudentSearchCriteria composite = new CompositeStudentSearchCriteria(SearchOperation.INTERSECTION);
|
||||||
|
composite.add(new GroupNumberRangeCriteria("A10", "C30"));
|
||||||
|
composite.add(new AverageGradeRangeCriteria(4.0, 5.0));
|
||||||
|
Student student = new Student("B20", 4.5, 123);
|
||||||
|
assertTrue(composite.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompositeIntersectionOneFalse() {
|
||||||
|
CompositeStudentSearchCriteria composite = new CompositeStudentSearchCriteria(SearchOperation.INTERSECTION);
|
||||||
|
composite.add(new GroupNumberRangeCriteria("A10", "C30"));
|
||||||
|
composite.add(new AverageGradeRangeCriteria(4.0, 5.0));
|
||||||
|
Student student = new Student("B20", 3.5, 123);
|
||||||
|
assertFalse(composite.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompositeUnionOneTrue() {
|
||||||
|
CompositeStudentSearchCriteria composite = new CompositeStudentSearchCriteria(SearchOperation.UNION);
|
||||||
|
composite.add(new GroupNumberRangeCriteria("A10", "C30"));
|
||||||
|
composite.add(new AverageGradeRangeCriteria(4.0, 5.0));
|
||||||
|
Student student = new Student("D00", 4.5, 123);
|
||||||
|
assertTrue(composite.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompositeUnionAllFalse() {
|
||||||
|
CompositeStudentSearchCriteria composite = new CompositeStudentSearchCriteria(SearchOperation.UNION);
|
||||||
|
composite.add(new GroupNumberRangeCriteria("A10", "C30"));
|
||||||
|
composite.add(new AverageGradeRangeCriteria(4.0, 5.0));
|
||||||
|
Student student = new Student("D00", 3.5, 123);
|
||||||
|
assertFalse(composite.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCompositeWithNestedComposite() {
|
||||||
|
CompositeStudentSearchCriteria outer = new CompositeStudentSearchCriteria(SearchOperation.INTERSECTION);
|
||||||
|
outer.add(new GroupNumberRangeCriteria("A10", "C30"));
|
||||||
|
|
||||||
|
CompositeStudentSearchCriteria inner = new CompositeStudentSearchCriteria(SearchOperation.UNION);
|
||||||
|
inner.add(new AverageGradeRangeCriteria(4.0, 5.0));
|
||||||
|
inner.add(new RecordBookNumberRangeCriteria(100, 200));
|
||||||
|
|
||||||
|
outer.add(inner);
|
||||||
|
|
||||||
|
Student student = new Student("B20", 4.5, 150);
|
||||||
|
assertTrue(outer.matches(student));
|
||||||
|
student = new Student("B20", 3.5, 150);
|
||||||
|
assertFalse(outer.matches(student));
|
||||||
|
student = new Student("D00", 4.5, 150);
|
||||||
|
assertFalse(outer.matches(student));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServiceWithSingleCriteria() {
|
||||||
|
MyList<Student> students = new MyLinkedList<>();
|
||||||
|
students.add(new Student("A10", 4.5, 1));
|
||||||
|
students.add(new Student("B20", 3.8, 2));
|
||||||
|
students.add(new Student("C30", 4.9, 3));
|
||||||
|
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
StudentSearchService service = new StudentSearchService(criteria);
|
||||||
|
MyList<Student> result = service.find(students);
|
||||||
|
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
assertEquals("A10", result.get(0).getGroupNumber());
|
||||||
|
assertEquals("C30", result.get(1).getGroupNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServiceEmptyResult() {
|
||||||
|
MyList<Student> students = new MyLinkedList<>();
|
||||||
|
students.add(new Student("A10", 3.5, 1));
|
||||||
|
students.add(new Student("B20", 3.8, 2));
|
||||||
|
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
StudentSearchService service = new StudentSearchService(criteria);
|
||||||
|
MyList<Student> result = service.find(students);
|
||||||
|
|
||||||
|
assertEquals(0, result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServiceEmptyCollection() {
|
||||||
|
MyList<Student> students = new MyLinkedList<>();
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
StudentSearchService service = new StudentSearchService(criteria);
|
||||||
|
MyList<Student> result = service.find(students);
|
||||||
|
assertEquals(0, result.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServicePreservesOrder() {
|
||||||
|
MyList<Student> students = new MyLinkedList<>();
|
||||||
|
students.add(new Student("Z00", 4.5, 1));
|
||||||
|
students.add(new Student("A10", 4.5, 2));
|
||||||
|
students.add(new Student("M30", 4.5, 3));
|
||||||
|
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
StudentSearchService service = new StudentSearchService(criteria);
|
||||||
|
MyList<Student> result = service.find(students);
|
||||||
|
|
||||||
|
assertEquals(3, result.size());
|
||||||
|
assertEquals("Z00", result.get(0).getGroupNumber());
|
||||||
|
assertEquals("A10", result.get(1).getGroupNumber());
|
||||||
|
assertEquals("M30", result.get(2).getGroupNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServiceDoesNotModifyOriginal() {
|
||||||
|
MyList<Student> original = new MyLinkedList<>();
|
||||||
|
original.add(new Student("A10", 4.5, 1));
|
||||||
|
original.add(new Student("B20", 3.8, 2));
|
||||||
|
|
||||||
|
MyList<Student> copy = new MyLinkedList<>();
|
||||||
|
for (int i = 0; i < original.size(); i++) {
|
||||||
|
copy.add(original.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
StudentSearchService service = new StudentSearchService(criteria);
|
||||||
|
service.find(original);
|
||||||
|
|
||||||
|
assertEquals(copy.size(), original.size());
|
||||||
|
for (int i = 0; i < copy.size(); i++) {
|
||||||
|
assertEquals(copy.get(i).getGroupNumber(), original.get(i).getGroupNumber());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServiceWithNullCriteriaThrowsException() {
|
||||||
|
assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> new StudentSearchService(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServiceWithCompositeCriteria() {
|
||||||
|
MyList<Student> students = new MyLinkedList<>();
|
||||||
|
students.add(new Student("A10", 4.5, 1));
|
||||||
|
students.add(new Student("B20", 4.5, 2));
|
||||||
|
students.add(new Student("C30", 3.5, 3));
|
||||||
|
|
||||||
|
CompositeStudentSearchCriteria composite = new CompositeStudentSearchCriteria(SearchOperation.INTERSECTION);
|
||||||
|
composite.add(new GroupNumberRangeCriteria("A10", "B20"));
|
||||||
|
composite.add(new AverageGradeRangeCriteria(4.0, 5.0));
|
||||||
|
|
||||||
|
StudentSearchService service = new StudentSearchService(composite);
|
||||||
|
MyList<Student> result = service.find(students);
|
||||||
|
|
||||||
|
assertEquals(2, result.size());
|
||||||
|
assertEquals("A10", result.get(0).getGroupNumber());
|
||||||
|
assertEquals("B20", result.get(1).getGroupNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testSearchServiceWithNullStudentListReturnsEmpty() {
|
||||||
|
StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0);
|
||||||
|
StudentSearchService service = new StudentSearchService(criteria);
|
||||||
|
MyList<Student> result = service.find(null);
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals(0, result.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
# Техническое задание: генератор случайных студентов
|
||||||
|
|
||||||
|
## Задача
|
||||||
|
|
||||||
|
Реализовать класс для формирования `MyList<Student>` со случайными значениями.
|
||||||
|
|
||||||
|
По умолчанию ограничения диапазона для генерации не задаются. В таком случае значения должны генерироваться во всём допустимом диапазоне, установленном правилами класса `Student`.
|
||||||
|
|
||||||
|
Генерация объектов должна выполняться через существующий `StudentBuilder`.
|
||||||
|
|
||||||
|
## Название класса
|
||||||
|
|
||||||
|
```text id="m9s0q1"
|
||||||
|
RandomStudentGenerator
|
||||||
|
```
|
||||||
|
|
||||||
|
## Контракт класса
|
||||||
|
|
||||||
|
Класс должен хранить текущие ограничения генерации для каждого поля `Student`.
|
||||||
|
|
||||||
|
Для каждого поля необходимо предоставить по два метода: левое и правое ограничение.
|
||||||
|
|
||||||
|
### `groupNumber`
|
||||||
|
|
||||||
|
Тип поля: `String`.
|
||||||
|
|
||||||
|
```java id="t2z8k4"
|
||||||
|
RandomStudentGenerator setMinGroupNumber(String minGroupNumber);
|
||||||
|
```
|
||||||
|
|
||||||
|
```java id="q7x5n1"
|
||||||
|
RandomStudentGenerator setMaxGroupNumber(String maxGroupNumber);
|
||||||
|
```
|
||||||
|
|
||||||
|
Ограничения применяются при генерации `groupNumber`.
|
||||||
|
|
||||||
|
Сравнение границ выполнять в соответствии с правилами, используемыми для `groupNumber` в `Student`.
|
||||||
|
|
||||||
|
### `averageGrade`
|
||||||
|
|
||||||
|
Тип поля: `double`.
|
||||||
|
|
||||||
|
```java id="v4n2j8"
|
||||||
|
RandomStudentGenerator setMinAverageGrade(double minAverageGrade);
|
||||||
|
```
|
||||||
|
|
||||||
|
```java id="r5c1p9"
|
||||||
|
RandomStudentGenerator setMaxAverageGrade(double maxAverageGrade);
|
||||||
|
```
|
||||||
|
|
||||||
|
Диапазон должен быть включительным:
|
||||||
|
|
||||||
|
```text id="u3k7w2"
|
||||||
|
minAverageGrade <= averageGrade <= maxAverageGrade
|
||||||
|
```
|
||||||
|
|
||||||
|
### `recordBookNumber`
|
||||||
|
|
||||||
|
Тип поля: `int`.
|
||||||
|
|
||||||
|
```java id="d8m4x6"
|
||||||
|
RandomStudentGenerator setMinRecordBookNumber(int minRecordBookNumber);
|
||||||
|
```
|
||||||
|
|
||||||
|
```java id="e1q9s5"
|
||||||
|
RandomStudentGenerator setMaxRecordBookNumber(int maxRecordBookNumber);
|
||||||
|
```
|
||||||
|
|
||||||
|
Диапазон должен быть включительным:
|
||||||
|
|
||||||
|
```text id="y6p2v8"
|
||||||
|
minRecordBookNumber <= recordBookNumber <= maxRecordBookNumber
|
||||||
|
```
|
||||||
|
|
||||||
|
## Метод генерации
|
||||||
|
|
||||||
|
Основной метод:
|
||||||
|
|
||||||
|
```java id="k3w7m1"
|
||||||
|
MyList<Student> generate(int size);
|
||||||
|
```
|
||||||
|
|
||||||
|
Контракт:
|
||||||
|
|
||||||
|
- создаёт ровно `size` объектов `Student`;
|
||||||
|
- все объекты должны быть валидными;
|
||||||
|
- каждый объект создаётся через `StudentBuilder`;
|
||||||
|
- результат возвращается как `MyList<Student>`;
|
||||||
|
- стандартные `List`, `ArrayList`, `LinkedList` и другие контейнеры использовать нельзя.
|
||||||
|
|
||||||
|
## Поведение диапазонов по умолчанию
|
||||||
|
|
||||||
|
Если для поля ограничение не задано, используется весь допустимый диапазон этого поля.
|
||||||
|
|
||||||
|
Необходимо учитывать существующие ограничения `Student`, в частности:
|
||||||
|
|
||||||
|
```text id="z4r8t2"
|
||||||
|
averageGrade:
|
||||||
|
getMinAverageGrade()
|
||||||
|
getMaxAverageGrade()
|
||||||
|
|
||||||
|
recordBookNumber:
|
||||||
|
getMinRecordBookNumber()
|
||||||
|
```
|
||||||
|
|
||||||
|
Для `groupNumber` использовать существующий шаблон:
|
||||||
|
|
||||||
|
```text id="n5c7y3"
|
||||||
|
getGroupNumberPattern()
|
||||||
|
```
|
||||||
|
|
||||||
|
Генерируемое значение `groupNumber` должно соответствовать требованиям `Student`.
|
||||||
|
|
||||||
|
## Проверка ограничений
|
||||||
|
|
||||||
|
При установке границ необходимо проверять их корректность.
|
||||||
|
|
||||||
|
Недопустимые случаи:
|
||||||
|
|
||||||
|
```text id="h2k6p4"
|
||||||
|
min > max
|
||||||
|
```
|
||||||
|
|
||||||
|
Для них выбрасывать:
|
||||||
|
|
||||||
|
```java id="c7v3n9"
|
||||||
|
IllegalArgumentException
|
||||||
|
```
|
||||||
|
|
||||||
|
Ограничение также не должно выходить за допустимые значения `Student`.
|
||||||
|
|
||||||
|
## Рекомендуемая реализация
|
||||||
|
|
||||||
|
Класс должен хранить шесть ограничений:
|
||||||
|
|
||||||
|
```text id="b5q9x2"
|
||||||
|
minGroupNumber
|
||||||
|
maxGroupNumber
|
||||||
|
|
||||||
|
minAverageGrade
|
||||||
|
maxAverageGrade
|
||||||
|
|
||||||
|
minRecordBookNumber
|
||||||
|
maxRecordBookNumber
|
||||||
|
```
|
||||||
|
|
||||||
|
Для каждого поля значения генерируются независимо.
|
||||||
|
|
||||||
|
Рекомендуется использовать стандартный генератор случайных чисел Java, например:
|
||||||
|
|
||||||
|
```java id="a8f4k1"
|
||||||
|
Random
|
||||||
|
```
|
||||||
|
|
||||||
|
или:
|
||||||
|
|
||||||
|
```java id="p3m7d5"
|
||||||
|
ThreadLocalRandom
|
||||||
|
```
|
||||||
|
|
||||||
|
При создании `Student`:
|
||||||
|
|
||||||
|
```text id="w6j2r9"
|
||||||
|
сгенерировать поля
|
||||||
|
↓
|
||||||
|
StudentBuilder
|
||||||
|
↓
|
||||||
|
build()
|
||||||
|
↓
|
||||||
|
Student
|
||||||
|
↓
|
||||||
|
MyList<Student>
|
||||||
|
```
|
||||||
|
|
||||||
|
Дополнительную валидацию, дублирующую правила `Student`, в генераторе не реализовывать.
|
||||||
|
|
||||||
|
## Примеры использования
|
||||||
|
|
||||||
|
Без настройки диапазонов:
|
||||||
|
|
||||||
|
```java id="s2k8m4"
|
||||||
|
RandomStudentGenerator generator =
|
||||||
|
new RandomStudentGenerator();
|
||||||
|
|
||||||
|
MyList<Student> students =
|
||||||
|
generator.generate(100);
|
||||||
|
```
|
||||||
|
|
||||||
|
С настройкой диапазонов:
|
||||||
|
|
||||||
|
```java id="f7q3n1"
|
||||||
|
RandomStudentGenerator generator =
|
||||||
|
new RandomStudentGenerator()
|
||||||
|
.setMinGroupNumber("A00")
|
||||||
|
.setMaxGroupNumber("Z99")
|
||||||
|
.setMinAverageGrade(3.0)
|
||||||
|
.setMaxAverageGrade(5.0)
|
||||||
|
.setMinRecordBookNumber(10000)
|
||||||
|
.setMaxRecordBookNumber(99999);
|
||||||
|
|
||||||
|
MyList<Student> students =
|
||||||
|
generator.generate(100);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Тесты
|
||||||
|
|
||||||
|
### Генерация без ограничений
|
||||||
|
|
||||||
|
```java id="r1w5p8"
|
||||||
|
MyList<Student> result = generator.generate(100);
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить:
|
||||||
|
|
||||||
|
- размер равен `100`;
|
||||||
|
- все элементы не равны `null`;
|
||||||
|
- каждый `Student` валиден.
|
||||||
|
|
||||||
|
### Ограничение `averageGrade`
|
||||||
|
|
||||||
|
Задать:
|
||||||
|
|
||||||
|
```text id="x4n7k2"
|
||||||
|
3.0 — 4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить, что все значения находятся в указанном диапазоне.
|
||||||
|
|
||||||
|
### Ограничение `recordBookNumber`
|
||||||
|
|
||||||
|
Задать:
|
||||||
|
|
||||||
|
```text id="q9m3v6"
|
||||||
|
10000 — 20000
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить диапазон и включение границ.
|
||||||
|
|
||||||
|
### Ограничение `groupNumber`
|
||||||
|
|
||||||
|
Задать левую и правую границы.
|
||||||
|
|
||||||
|
Проверить, что все сгенерированные значения находятся между ними согласно правилам сравнения `groupNumber`.
|
||||||
|
|
||||||
|
### Граничные значения
|
||||||
|
|
||||||
|
Установить:
|
||||||
|
|
||||||
|
```text id="p8c4s1"
|
||||||
|
min == max
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверить, что все сгенерированные значения равны указанной границе.
|
||||||
|
|
||||||
|
### Некорректный диапазон
|
||||||
|
|
||||||
|
Проверить выброс `IllegalArgumentException`, если:
|
||||||
|
|
||||||
|
```text id="z6t2h5"
|
||||||
|
min > max
|
||||||
|
```
|
||||||
|
|
||||||
|
### Некорректное ограничение
|
||||||
|
|
||||||
|
Передать диапазон, выходящий за допустимые значения `Student`.
|
||||||
|
|
||||||
|
Проверить выброс исключения.
|
||||||
|
|
||||||
|
### Генерация пустой коллекции
|
||||||
|
|
||||||
|
Для:
|
||||||
|
|
||||||
|
```java id="j5r8w3"
|
||||||
|
generate(0)
|
||||||
|
```
|
||||||
|
|
||||||
|
ожидается пустой `MyList<Student>`.
|
||||||
|
|
||||||
|
### Некорректный размер
|
||||||
|
|
||||||
|
Для отрицательного размера:
|
||||||
|
|
||||||
|
```java id="n2k7p4"
|
||||||
|
generate(-1)
|
||||||
|
```
|
||||||
|
|
||||||
|
ожидается `IllegalArgumentException`.
|
||||||
|
|
||||||
|
## Результат
|
||||||
|
|
||||||
|
Необходимо реализовать:
|
||||||
|
|
||||||
|
```text id="v8m3q6"
|
||||||
|
RandomStudentGenerator.java
|
||||||
|
```
|
||||||
|
|
||||||
|
Класс должен использовать существующие:
|
||||||
|
|
||||||
|
```text id="f1j9x4"
|
||||||
|
Student.java
|
||||||
|
StudentBuilder.java
|
||||||
|
MyList.java
|
||||||
|
```
|
||||||
|
|
||||||
|
Изменять интерфейсы `Student`, `StudentBuilder` и `MyList` для выполнения данного задания не требуется.
|
||||||
Reference in New Issue
Block a user