Рубрики
Без рубрики

Простая манипуляция коллекцией в Java с использованием Лямбда-выражений

Одной из самых мощных функций, представленных в Java 8, была Лямбда, Хотя на первый взгляд это может показаться незначительным, новая функция во многих случаях ускоряет как кодирование, так и выполнение, если она используется…

Автор оригинала: Petre Popescu.

Одной из самых мощных функций, представленных в Java 8, была Lambda, Хотя на первый взгляд это может показаться незначительным, новая функциональность во многих случаях ускоряет как кодирование, так и выполнение, если ее правильно использовать. Здесь мы рассмотрим возможности потоков и лямбда-выражений в Java и будем использовать их для выполнения манипуляций с коллекциями. Это ни в коем случае не продвинутый учебник, а введение в эту функциональность. Надеюсь, к тому времени, когда вы дойдете до конца, вы поделитесь какой-нибудь новой информацией.

Начальный класс

Мы начнем с простого класса для учащихся, в котором будут сохранены их имя, возраст и несколько оценок. Это поможет нам в последующих примерах.

public class Student {
    private String name;
    private double gradeAtMath;
    private double gradeAtEnglish;
    private int age;
    public boolean passed() {
        return (gradeAtMath + gradeAtEnglish) / 2 >= 5;
    }
    // Getters and Setters
}

Stream.filter() в Java

Давайте начнем с чего-нибудь маленького и легкого. У нас есть коллекция (в данном примере Список, но это может быть любая коллекция Java), в которой хранятся студенты в университете. Теперь мы хотим извлечь тех, чье имя начинается на букву “М”.

List filtered= students.stream()
        .filter(s -> s.getName().toUpperCase(Locale.ROOT).startsWith("M"))
        .collect(Collectors.toList());

Давайте разберемся с этим. Сначала мы создаем поток из нашей коллекции. Затем, используя filter (), мы предоставляем лямбда-выражение, в котором получаем имя учащегося, делая его прописным и проверяя, начинается ли оно с “М”. В последнюю очередь мы собираем результаты в новый список. Поначалу это может показаться незначительным, поскольку мы можем добиться того же эффекта, используя более традиционные методы, однако мы только начинаем. Мы можем объединить несколько фильтров в цепочку, чтобы выполнять более сложные операции без необходимости писать большой и нечеткий оператор.

List filtered = students.stream()
        .filter(s -> s.getName().toUpperCase(Locale.ROOT).startsWith("M"))
        .filter(s -> s.getGradeAtMath() >= 5)
        .filter(s -> s.getGradeAtEnglish() < 5)
        .filter(Student::passed)
        .collect(Collectors.toList());

Здесь мы цепляем несколько фильтров, чтобы получить точные результаты, которые мы ищем. Интересным примером является последний фильтр, где нам нужно только указать метод, который возвращает наше значение интереса, в данном случае, если он прошел год.

Сделайте несколько фильтров () вызовы приводят к увеличению времени выполнения?

Этот вопрос задают много раз, и на первый взгляд может показаться, что выполнение нескольких вызовов filter() неэффективно. Однако это не всегда так. В зависимости от используемых предикатов компилятор и JVM могут выполнять оптимизацию, которая может привести к одинаковому или даже лучшему времени выполнения. Это связано с тем, что для потоков вызов функции filter() дважды НЕ означает, что первый вызов выполняется для исходной коллекции, а второй-для результирующей коллекции. Потоки имеют несколько конвейеров, которые выполняют промежуточные и конечные операции. Обход источника трубопровода не начинается до тех пор, пока не будет выполнена конечная операция трубопровода.

Одна из оптимизаций, которую вы можете выполнить, чтобы гарантировать более быстрое выполнение, – это использовать ссылку на метод везде, где это возможно, вместо лямбда-выражения. Если мы используем .фильтр(Студент::пройден) даст меньше создаваемых объектов, чем .фильтр(s -> s.пройдено()) так что в результате более быстрое время выполнения. В большинстве случаев разница во времени выполнения между сложным предикатом и последовательными вызовами нескольких фильтров, каждый из которых имеет более простой предикат, незначительна. Моя рекомендация-выбрать тот, который легче читать и понимать.

Поток.сортировка()

Еще одна полезная функция-сортировка коллекции. Это достигается с помощью метода sort (), и, как и для filter (), мы можем использовать лямбда-выражения для упрощения нашей работы. Ниже мы будем сортировать студентов по именам. Оба примера делают одно и то же, но я привел их оба, чтобы показать, как можно использовать лямбды и компараторы для достижения более чистого кода.

List sorted = students.stream()
        .sorted((s1, s2) -> s1.getName().compareTo(s2.getName()))
        .collect(Collectors.toList());
List sorted= students.stream()
        .sorted(Comparator.comparing(Student::getName))
        .collect(Collectors.toList());

Во втором примере мы предоставляем компаратор, который сравнивает возврат метода getName (). Мощь функций сортировки заключается в классе компараторов. Нередко у студентов одно и то же имя, поэтому давайте предположим, что при сортировке по имени мы хотим, чтобы человек с наивысшей оценкой по математике был первым с тем же именем. В более традиционных механизмах это было бы довольно сложной задачей, особенно если бы мы хотели иметь еще больше критериев. В Java 8, благодаря мощности компаратора, этого можно легко достичь, объединив несколько компараторов и применив полученный результат к функции sort ().

| ИМЯ | Оценка по математике | Оценка по английскому языку | Возраст | +------+---------------+------------------+------+ |Том| 8 | 7 | 3 Джерри| 9 | 5 | 2

И код

List students = getAllStudents();
Comparator comparator = Comparator.comparing(Student::getName)
        .thenComparing(Student::getGradeAtMath);
students.stream().sorted(comparator);

Выход:

| ИМЯ | Оценка по математике | Оценка по английскому языку | Возраст | +------+---------------+------------------+------+ |Джерри| 9 | 5 | 2 Спайк| 7 | 9 | 4

У нас может быть столько вызовов функции сравнения (), сколько необходимо. В реальных сценариях, где у нас много данных, вероятность того, что две или более записей будут иметь одинаковые значения для некоторых полей, увеличивается. Цепные компараторы позволят нам иметь точный порядок, когда такие столкновения существуют.

Эта статья была первоначально опубликована на моем веб-сайте. Там вы можете найти более подробную информацию и примеры по Потоки Java и манипуляции с коллекциями .

Оригинал: “https://www.codementor.io/@petrepopescu/simple-collection-manipulation-in-java-using-lambdas-1jv5lx0ul8”