Технологии программирования
Лекция 8. Collections. Streams API. Lambda-выражения.
Содержание
Collections
Java Collection Framework
Collection Простые наборы элементов.
Базовый набор методов (size(), isEmpty(), add(E e) и др - см доку)
В интерфейсе уже есть дефолтные реализации.
Работа с типами данных ключ - значение. В c# - аналог Dictionary, в питоне - словари.
Базовый набор методов (containsKey(Object key), get(Object key), isEmpty(), size())
Map
import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Scanner;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class RoomSearchServiceImpl implements RoomSearchService {
HashMap<Integer, String[]> info = new HashMap<>();
HashMap<Integer, int[]> busy = new HashMap<>();
@Override
public void init(String fileName) {
RuntimeException runtimeException = new RuntimeException("error: cant initialize");
try {
File file = new File(fileName);
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] parts = line.split(";");
if (parts.length != 10) {
throw runtimeException;
}
int id = Integer.parseInt(parts[0]);
info.put(id, Arrays.copyOfRange(parts, 1, 4));
int[] busyRooms = Arrays.stream(Arrays.copyOfRange(parts, 4, 10))
.mapToInt(Integer::parseInt)
.toArray();
busy.put(id, busyRooms);
}
scanner.close();
} catch (FileNotFoundException e) {
throw runtimeException;
}
}
@Override
public List<Integer> getFreeRooms(int lessonNumber) {
return info.entrySet()
.stream()
.filter(e -> busy.get(e.getKey())[lessonNumber - 1] == 0)
.map(e -> Integer.parseInt(e.getValue()[0]))
.toList();
}
@Override
public List<Integer> getRoomForPractise(int studentsCount) {
return info.entrySet()
.stream()
.filter(e -> Integer.parseInt(e.getValue()[1]) >= studentsCount
&& Arrays.stream(busy.get(e.getKey())).anyMatch(value -> value == 0))
.map(e -> Integer.parseInt(e.getValue()[0]))
.toList();
}
@Override
public Optional<Integer> getMostUnusedRoom() {
HashMap<Integer, Integer> countBusy = new HashMap<>();
for (Integer id : info.keySet()) {
int count = 0;
for (int num : busy.get(id)) {
count += num;
}
countBusy.put(id, count);
}
return countBusy.entrySet()
.stream()
.min(Map.Entry.comparingByValue())
.map(e -> Integer.parseInt(info.get(e.getKey())[0]));
}
}
List
import java.util.ArrayList;
import java.util.LinkedList;
class Temp {
public static void main(String[] args) {
// Vector - синхронизирован. Будем использовать ArrayList
// Stack - LIFO, но лучше использовать ArrayDeque
// ArrayList - список основан на массиве
ArrayList<String> ourList = new ArrayList<String>();
ourList.size();
ourList.add("");
ourList.remove(0);
ourList.toArray(new String[]{});
ourList.addAll(new ArrayList<>());
// LinkedList - связный
}
}
import java.util.HashSet;
class Temp {
public static void main(String[] args) {
// HashSet - Set, базирующийся на HashMap. Порядок не гарантируется.
HashSet<String> set = new HashSet<>();
set.add("123");
set.add("123");
set.add("123");
set.size();
set.remove("123");
// LinkedHashSet - базируется на LinkedHashMap
// TreeSet - специфичный сет, можно управлять порядком элементов и использовать Comparator
}
}
Queue
import java.util.ArrayDeque;
class Temp {
public static void main(String[] args) {
// PriorityQueue - очередь FIFO, не поддерживает null, можно управлять порядком
// через Comparator
// ArrayDeque - LIFO (stack), быстрее чем Stack и LinkedList
ArrayDeque<String> stack = new ArrayDeque<>();
stack.addFirst("123");
stack.pollFirst(); // возвращает первый элемент и удаляет из коллекции
}
}
Более распространенные коллекции: HashMap, HashSet, ArrayList, LinkedList, ArrayDeque
Streams API
// используем лямбды в сортировках
public class Temp {
public static void main(String[] args) {
// Запись без Streams API
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(5);
list.add(6);
list.add(7);
List<Integer> newList = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
if (list.get(i) % 2 == 0 && newList.size() <= 3) {
newList.add(list.get(i) + 1);
}
}
// Тоже самое с Streams API
int[] array = IntStream.of(1, 2, 5, 6, 7) // id пользователей
.filter(id -> id % 2 == 0)
.map(Math::abs) // здесь по идее должны были написать .map(x -> Math.abs(x)), но когда
// у функции один параметр - можем просто указать ссылку на метод
.limit(3)
.toArray();
// Можем фильтровать содержимое списка и находить, например первый элемент
Optional<Integer> opt = list.stream()
.filter(i -> i < 0)
.findFirst();
// Удобно работать с коллекциями объектов
List<Student> students = new ArrayList<>();
students.add(new Student(5.0, "Ivanets AS"));
students.add(new Student(4.5, "Dodonov NYou"));
students.add(new Student(4.0, "Petrov AS"));
students.add(new Student(3.8, "Ivanov AS"));
Stream<Student> stream = students.stream();
// Например, можем строить условия с полями объектов и преобразовывать поток объектов
// в поток строк и собирать их в список. Например, можем собрать ФИО студентов отличников для
// формирования приказа на назначение стипендии:
List<String> paymentFios = students.stream()
.filter(student -> student.middleMark >= 4)
.map(student -> student.fio)
.collect(Collectors.toList()); // toList() - в современной джаве
List<Student> actualStudents = new ArrayList<>();
// Можем поделить студентов на две группы - отчисленных в результате сессии и нет
Map<String, List<Student>> splitForExpulsionStudents = students.stream()
.map(student -> new Pair<>(student.middleMark <= 2 ? "expel" : "not expel", student))
.collect(Collectors.groupingBy(Pair::getValue0, Collectors.mapping(Pair::getValue1, Collectors.toList())));
// Или сгруппировать студентов по средней оценке
Map<Double, List<Student>> middleMark = students.stream()
.collect(Collectors.groupingBy(student -> student.middleMark));
}
static class Student {
int id;
double middleMark;
String fio;
Student(double middleMark, String fio) {
this.middleMark = middleMark;
this.fio = fio;
}
}
}
Lambda-выражения
Lambda-выражений в Java позволяют создавать анонимные функции, которые можно использовать для работы с коллекциями и для передачи поведения в качестве аргумента другим методам.
Лямбда-выражения - это короткие анонимные функции в Java, которые позволяют выполнять различные операции с данными. Их можно использовать для работы с коллекциями и для передачи поведения в качестве аргумента другим методам. Вот как это выглядит:
(параметры) -> {
операторы;
return возвращаемое значение;
}
Параметры - это то, что мы передаем в нашу мини-программу, например числа или строки. Операторы - это то, что наша мини-программа делает с параметрами. Возвращаемое значение - это то, что возвращается после выполнения операторов.
Например, если мы хотим создать мини-программу для сложения двух чисел, мы можем написать:
(число1, число2) -> число1 + число2
Это означает, что наша программа принимает два числа (число1 и число2) и возвращает их сумму. Мы можем использовать эту лямбда-функцию для сложения любых двух чисел.
В следующем выражении мы создаем функцию возведения в квадрат при помощи lambda. Функция принимает на вход число и возвращает его квадрат.
Function<Integer, Integer> pow = x -> x * x;
var result = pow.apply(5);