Технологии Java

Лямбда-выражения и потоки

Лямбда-выражения

Содержание

Функциональный интерфейс

Лямбда-выражения

  • Реализация функционального интерфейса
    • R BiFunction<T, U, R>.apply(t, u) – бинарная функция
    • BiFunction<String, Long, String> f =
          (String s, Long v) −> {
              return s + v;
          }
      
    • BiFunction<String, Long, String> f =
          (String s, Long v) −> s + v;
      
    • BiFunction<String, Long, String> f = (s, v) −> s + v;
      
    • Function<String, String> f = s −> s + s;
      
    • Supplier<String> s = () −> "!";
      
    • Consumer<String> c = s −> System.out.println(s + s);
      

Применение лямбда-выражений

  • Композиция
    • default <V> Function<V, R>
              compose(Function<−V, +T> before) {
          return v −> apply(before.apply(v));
      }
      
  • Обратная композиция
    • default <V> Function<T, V>
              andThen(Function<−R, +V> after) {
          return v −> after.apply(apply(v));
      }
      

Ссылки на методы

  • Метод класса
    • Function<String, Integer> f1 = Integer::parseInt;
      // x −> Integer.parseInt(x);
      
  • Метод экземпляра
    • Function<Integer, String> f2 = Integer::toString;
      // x −> x.toString();
      
  • Метод экземпляра объекта
    • Integer i = 2; // Не обязательно final
      Supplier<String> f3 = i::toString; // () −> i.toString();
      
  • Конструктор
    • Function<String, Integer> f = Integer::new;
      // s −> new Integer(s);
      

Замыкания

  • Effectively final переменные
    • Ровно одно присваивание
    • Может быть без модификатора final
    • Могут использоваться в лямбда-выражениях
  • Замыкание → объект в куче
    • Разные вызовы → разные объекты
  • Пример
    • /* final */ String hello = "Hello, ";
      Function<String, String> greeter = 
              name −> hello + name;
      

Ограничения лямбда-выражений

  • Захват изменяемых переменных
  • Прозрачный проброс исключений
    • // Не работает, так как write бросает IOException
      Consumer<String> c = writer::write;
      
  • Изменение потока управления
    • collection.forEach(e −> {
          if (e.equals("done")) {
              // Что делать?
          }
      });
      
  • Реализация
    • Лямбды — не классы

Пример. Исключения ввода-вывода

  • @FunctionalInterface
    interface IOSupplier<T> {
        T get() throws IOException;
        static <T> T unchecked(IOSupplier<T> supplier) {
            try {
                return supplier.get();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }
    }
    
  • List<String> lines = IOSupplier.unchecked(() −>
        Files.readAllLines(Path.of("input.txt")));
    

Основные классы

Содержание

Функции

Основные функц. интерфейсы

Специализации для примитивов

Типы-обертки

Беззнаковые целые

Класс Objects

Коллекции

Содержание

Необязательные значения (1)

Необязательные значения (2)

Сравнения

  • Статические методы интерфейса Comparator
  • Пример
    • courses.sort(
          Comparator.comparingInt(Course::getYear)
              .thenComparingInt(Course::getTerm)
              .thenComparing(
                  Course::getName,
                  String.CASE_INSENSITIVE_ORDER
      ))
      

Коллекции

Отображения

Пример. Подсчет слов

  • Из Scanner
    • Map<String, Integer> map = new HashMap<>();
      while (scanner.hasNext()) {
          map.merge(scanner.next(), 1, Integer::sum);
      }
      
  • Из файла
    • Map<String, Integer> map = new HashMap<>();
      Files.readAllLines(input).forEach(line −> {
          for (String word : line.split("\\s+")) {
              map.merge(word, 1, Integer::sum);
          }
      });
      

Потоки значений

  • Классы Stream<T>, {Int|Long|Double}Stream
  • Набор элементов, обрабатываемый оптом
    • Может не хранить элементы
    • Может быть ленивым
    • Может быть бесконечным
  • Получение
    • Из коллекций и массивов
    • Генераторы
    • Строки из файлов и наборы файлов
    • Случайные потоки

Примеры использования потоков (1)

  • Что вычисляется?
    • string.stream()
          .filter(s −> s.endsWith("s"))
          .mapToInt(String::length)
          .min();
      
    • Длина самой короткой строки, оканчивающейся на s
  • Что вычисляется?
    • string.parallelStream()
          .filter(s −> s.contains("a"))
          .sorted(String.CASE_INSENSITIVE_ORDER)
          .limit(3)
          .collect(Collectors.joining(", "));
      
    • Три минимальных слова, содержащих a

Операции над потоками

  • Создание
  • Промежуточные
    • Порождают поток
    • Ленивые
  • Завершающие
    • Порождают значения
    • Жадные

Создание потоков (1)

Создание потоков (2)

Модификация потоков

Комбинаторы

Получение результата

Коллекторы

Статистика

Преобразование в коллекцию

Пример. Подсчет слов

  • В тексте
    • Map<String, Long> map = words.stream()
          .collect(Collectors.groupingBy(
              String::toUpperCase,
              Collectors.counting()
          ));
      
  • В файле
    • Map<String, Long> map = Files.lines(path)
          .flatMap(line −> Arrays.stream(line.split("\\s+"))
          .collect(Collectors.groupingBy(
              String::toUpperCase,
              Collectors.counting()
          ));
      

Комбинаторы

Преобразование в строку

Разбиваемые итераторы

Содержание

Разбиваемые итераторы

Иерархия изменяемости

  • Неизменяемый (List.of())
  • Управляющий изменениями (ConcurrentHashMap)
  • С детекцией изменений
    • Позднее связывание (ArrayList)
    • Раннее связывание
  • Без детекции изменений
    • Позднее связывание
    • Раннее связывание

Детекция изменений

  • Как бы вы реализовали?
  • Счётчик modCount
  • Итераторы
    • Что делать с remove()?
    • Обновить modCount итератора

Обработка изменений

  • Неизменяемый
  • Последовательно изменяемый
    • Явная политика связывания
    • Явная политика обработки изменений
  • Параллельно изменяемый

Свойства Spliterator

  • Свойства элементов
    • ORDERED – элементы упорядочены
    • SORTED – элементы отсортированы по getComparator()
    • DISTINCT – все элементы различны
    • NONNULL – не содержит null
  • Размеры
    • SIZED – знает свой размер
    • SUBSIZED – дети будут знать свой размер

Специализация для примитивов

Пример реализации

  • ArrayMap
    • Object[] entries
    • Хранит пары ключ-значение в 2i и 2i+1 элементах

Создание Spliterator

Потоки из Spliterator

Обобщенные свёртки

Коллекторы

Параллельное исполнение

  • Невмешательство (non-iterference)
    • Нельзя изменять обрабатываемые данные
  • Отсутствие состояния
    • Нельзя сохранять данные между вызовами
  • Без побочных эффектов
    • Нельзя изменять контекст
  • Упорядоченность
    • Результат – упорядочен
    • Вызовы – нет

Пример коллектора

  • Получение минимальных имени и фамилии
    • Collector<Student, String[], Map.Entry<String, String>>
          MIN_NAMES = Collector.of(
              () −> new String[2],
              (mins, Student) −> {
                  mins[0] = min(mins[0], Student.getFirstName());
                  mins[1] = min(mins[1], Student.getLastName());
              },
              (mins1, mins2) −> {
                  mins1[0] = min(mins1[0], mins2[0]);
                  mins1[1] = min(mins1[1], mins2[1]);
                  return mins1;
              },
              mins −> Map.entry(mins[0], mins[1])
          );
      

Заключение

Содержание

Что еще есть в java.util (1)

Что еще есть в java.util (2)

Ссылки

Вопросы

???