Skip to the content.

Технологии программирования

Назад на главную

Лекция 8. Collections. Streams API. Lambda-выражения.

Содержание

  1. Collections
  2. Streams API
  3. Lambda-выражения

Collections

Java Collection Framework

alt text

Collection Простые наборы элементов.

Базовый набор методов (size(), isEmpty(), add(E e) и др - см доку)

В интерфейсе уже есть дефолтные реализации.

Map

Работа с типами данных ключ - значение. В c# - аналог Dictionary, в питоне - словари.

Базовый набор методов (containsKey(Object key), get(Object key), isEmpty(), size())

Map

alt text

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

alt text

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 - связный
    }
}

alt text

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

alt text

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);

Полезные ссылки