Статьи

Почему Java 8?

Эта статья представлена ​​в Руководстве DZone по экосистеме Java . Получите бесплатную копию для более проницательных статей, отраслевой статистики и многого другого.

Быстрый просмотр:

  • Во многих случаях Java 8 улучшит производительность приложения без какой-либо конкретной работы или настройки.
  • Лямбда-выражения, Streams API и новые методы в существующих классах являются одними из ключевых улучшений производительности.
  • Новый Optionalтип Java 8 дает разработчикам значительную гибкость при работе с нулевыми значениями, уменьшая вероятность появления NullPointerExceptions

Java 8 была выпущена в начале прошлого года, а Java 7 сейчас в конце жизни, благодаря чему Java 8 стала единственным поддерживаемым Oracle вариантом, пока Java 9 не выйдет в конце следующего года. Тем не менее, поскольку организации ценят стабильность за модность, многие из нас все еще работают с Java 7 или даже 6. 

Давайте рассмотрим некоторые особенности Java 8 и предоставим некоторые аргументы, чтобы убедить вашу организацию перейти на новую версию.

Это быстрее

Вот пункт продажи, который может понравиться вашему боссу, бизнесу или операционистам: вы, вероятно, обнаружите, что Java 8 запускает ваше приложение быстрее. Вообще говоря, приложения, которые перешли на Java 8, видят некоторое улучшение скорости без какой-либо конкретной работы или настройки. Это может не относиться к приложению, которое было сильно настроено на конкретную JVM, но есть ряд причин, по которым Java 8 работает лучше:

Улучшения производительности в общих структурах данных: эталоны популярного HashMap показывают, что производительность в Java 8 выше. Такие улучшения очень убедительны — вам не нужно изучать новый Streams API или лямбда-синтаксис или даже изменять существующий код для улучшения скорости вашего приложения.

Улучшения сборщика мусора: часто «Производительность Java» является синонимом «Сборки мусора», и, безусловно, плохая производительность сборки мусора влияет на производительность приложения. Java 8 имеет существенные изменения в GC, которые улучшают производительность и упрощают настройку. Наиболее известным из этих изменений является удаление PermGen и внедрение Metaspace .

Вилка / Join скорость Улучшения: вилка / нарисуйте рамки нового в Java 7, и была последнее усилие , чтобы упростить параллельное программирование с использованием виртуальной машины Java. Много работы ушло на дальнейшее его улучшение для Java 8 . Fork / join теперь является фреймворком, который используется под прикрытиями для параллельных операций в Streams API (подробнее об этом позже). 

Кроме того, в Java 8 внесено гораздо больше изменений для поддержки параллелизма , и Oracle суммировала некоторые улучшения производительности в JDK 8 .

Меньше строк кода

Java регулярно обвиняют в том, что она сильно загружена стандартным кодом. Java 8 решает некоторые из этих проблем, применяя более функциональный стиль для новых API, сосредотачиваясь на том, чего вы хотите достичь, а не на том, как это сделать.

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

Лямбда-выражения в Java 8 — это не просто синтаксический сахар по сравнению с существующими анонимными внутренними классами Java — метод перед поведением, предшествующий Java 8. Lambdas используют преимущества встроенных изменений в Java 7 , поэтому они хорошо работают. Чтобы увидеть примеры того, как использование лямбда-выражений может упростить ваш код, читайте дальше.

Новые методы в наших любимых коллекциях

Хотя Lambdas и Streams (о которых мы расскажем далее), вероятно, являются двумя главными преимуществами Java 8, менее известно то, что изменения в Java 8 позволили разработчикам языка добавлять новые методы в существующие классы без ущерба для обратной совместимости. В результате новые методы в сочетании с лямбда-выражениями позволяют нам значительно упростить наш код. Возьмем, к примеру, общий случай выяснения, существует ли элемент на карте, и создание нового, если нет. До Java 8 вы могли бы написать что-то вроде:

private final Map<CustomerId, Customer> customers = new HashMap<>();

public void incrementCustomerOrders(CustomerId customerId) {
    Customer customer = customers.get(customerId);
    if (customer == null) {
        customer = new Customer(customerId);
        customers.put(customerId, customer);
    }
    customer.incrementOrders();
}

Эта операция «проверить, есть ли элемент на карте; если нет, то создать его и добавить его»настолько часто , что есть новый метод на Mapего поддержать: computeIfAbsent. Этот метод принимает в качестве второго аргумента лямбду, в которой указано, как создать отсутствующий элемент:

public void incrementCustomerOrders(CustomerId customerId) {
    Customer customer = customers.computeIfAbsent(customerId,
           id -> new Customer(id));
    customer.incrementOrders();
}

Фактически, в Java 8 есть еще одна новая функция, называемая ссылками на методы, которая делает это еще короче:

public void incrementCustomerOrders(CustomerId customerId) {
    Customer customer = customers.computeIfAbsent(customerId, Customer::new);
    customer.incrementOrders();
}

Mapи у Listобоих есть новые методы в Java 8. Стоит проверить их, чтобы увидеть, сколько строк кода они могут спасти.

API потоков

API Streams дает вам гибкость в запросе и обработке ваших данных. Это мощный инструмент. Проверьте некоторые из статей или книг по этой теме для более полного просмотра. Создание быстрых запросов для ваших данных интересно в мире больших данных, но так же полезно для обычных операций. Допустим, например, что у вас есть список книг, и вы хотите получить список уникальных авторов для этих книг в алфавитном порядке:

public List<Author> getAllAuthorsAlphabetically(List<Book> books) {
    List<Author> authors = new ArrayList<>();

    for (Book book : books) {
        Author author = book.getAuthor();
        if (!authors.contains(author)) {
            authors.add(author);
        }
    }
    Collections.sort(authors, new Comparator<Author>() {
        public int compare(Author o1, Author o2) {
            return o1.getSurname().compareTo(o2.getSurname());
        }
    });
    return authors;
}

В приведенном выше коде мы сначала перебираем список книг, добавляя автора книги в список авторов, если он еще не видел его; затем мы сортируем авторов в алфавитном порядке по фамилии. Это именно та операция, которую потоки были разработаны для элегантного решения:

public List<Author> getAllAuthorsAlphabetically(List<Book> books) {
    return books.stream()
                .map(book -> book.getAuthor())
                .distinct()
                .sorted((o1, o2) -> o1.getSurname().compareTo(o2.getSurname()))
                .collect(Collectors.toList());
}

Это не только меньшее количество строк кода, но и, возможно, более наглядное — разработчик, пришедший к этому коду позже, может прочитать его и понять, что 1) он получает авторов из книг, 2) его интересуют только уникальные авторы и 3) список возвращаемое отсортировано по фамилии автора. Объедините Streams API с другими новыми функциями — ссылками на методы и новыми методами в Comparator — и вы получите еще более лаконичную версию:

public List<Author> getAllAuthorsAlphabetically(List<Book> books) {
    return books.stream()
                .map(Book::getAuthor)
                .distinct()
                .sorted(Comparator.comparing(Author::getSurname))
                .collect(Collectors.toList());
}

Здесь еще более очевидно, что отсортированный метод упорядочивает по фамилии автора.

Легко распараллелить

Мы говорили о лучшей производительности из коробки, и в дополнение к упомянутым ранее функциям Java 8 может явно использовать больше ядер ЦП. Просто заменив поток методов в приведенных выше примерах parallelStream, JVM разделит операцию на отдельные задания и использует fork / join для их запуска на нескольких ядрах. Однако распараллеливание не волшебное заклинание, чтобы все было быстрее. Параллельное выполнение операций всегда требует больше работы — разделения операций и объединения результатов — и поэтому не всегда будет занимать меньше времени. Но этот вариант очень интересен для областей, которые подходят для распараллеливания.

Минимизировать нулевые указатели

Еще одна новая особенность Java 8 — новый Optionalтип. Этот тип является способом явного указания «У меня может быть значение или я могу быть нулевым». Это означает, что теперь API может быть явным о возврате значений, которые могут быть нулевыми, по сравнению со значениями, которые всегда будут ненулевыми, что сводит к минимуму вероятность попадания в NullPointerException

Что приятно, так Optionalэто то, как вы говорите, чтобы иметь дело с нулями. Например, если мы ищем определенную книгу в списке, новый findFirst()метод возвращает an Optional, который говорит нам, что не гарантируется, что найдется значение. Учитывая это необязательное значение, мы можем затем решить, что делать, если оно равно нулю. Если мы хотим создать специальное исключение, мы можем использовать orElseThrow:

public Book findBookByTitle(List<Book> books, String title) {
    Optional<Book> foundBook = books.stream()
           .filter(book -> book.getTitle().equals(title))
           .findFirst();
    return foundBook.orElseThrow(() -> new BookNotFoundException("Did not find book with title " + title));
}

Или вы можете вернуть другую книгу:

return foundBook.orElseGet(() -> getRecommendedAlternativeBook(title));

Или мы могли бы вернуть Optionalтак, чтобы вызывающие методы могли сами принимать решение о том, что делать, если книга не найдена.

В итоге

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

Чтобы получить больше информации о микросервисах, языках JVM и новых тенденциях в Java, получите бесплатную копию руководства DZone по экосистеме Java !