Перейти к основному содержимому

Java необязательно — orElse () против orElseGet ()

· 3 мин. чтения

Задача: Наибольшая подстрока без повторений

Для заданной строки s, найдите длину наибольшей подстроки без повторяющихся символов. Подстрока — это непрерывная непустая последовательность символов внутри строки...

ANDROMEDA 42

1. Введение

В API option обычно есть два метода, которые могут вызвать путаницу: orElse() и orElseGet() .

В этом кратком руководстве мы рассмотрим разницу между ними и узнаем, когда использовать каждый из них.

2. Подписи

Во-первых, давайте начнем с основ, взглянув на их подписи:

public T orElse(T other)

public T orElseGet(Supplier<? extends T> other)

Понятно, что orElse() принимает любой параметр типа T, тогда как orElseGet() принимает функциональный интерфейс типа Supplier , который возвращает объект типа T.

На основе их Javadocs :

  • orElse() : возвращает значение, если оно присутствует, в противном случае возвращает другое
  • orElseGet(): возвращает значение, если оно присутствует, в противном случае вызывает другое и возвращает результат своего вызова.

3. Отличия

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

3.1. илииначе()

Предполагая, что наш регистратор настроен правильно, давайте начнем с написания простого фрагмента кода:

String name = Optional.of("foreach")
.orElse(getRandomName());

Обратите внимание, что метод getRandomName() возвращает случайное имя из списка имен List<String> :

public String getRandomName() {
LOG.info("getRandomName() method - start");

Random random = new Random();
int index = random.nextInt(5);

LOG.info("getRandomName() method - end");
return names.get(index);
}

При выполнении нашего кода мы обнаружим следующие сообщения, напечатанные в консоли:

getRandomName() method - start
getRandomName() method - end

Имя переменной будет содержать «foreach» в конце выполнения кода.

С его помощью мы можем легко сделать вывод, что параметр orElse() оценивается, даже если у нас есть непустой Optional .

3.2. orElseGet()

Теперь давайте попробуем написать аналогичный код, используя orElseGet() :

String name = Optional.of("foreach")
.orElseGet(() -> getRandomName());

Приведенный выше код не вызывает метод getRandomName() .

Помните (из Javadoc), что метод Supplier , переданный в качестве аргумента, выполняется только в том случае , если необязательное значение отсутствует.

Поэтому использование orElseGet() для нашего случая сэкономит нам время, затрачиваемое на вычисление случайного имени .

4. Измерение влияния на производительность

Теперь, чтобы также понять различия в производительности, давайте воспользуемся JMH и посмотрим некоторые реальные цифры:

@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseBenchmark() {
return Optional.of("foreach").orElse(getRandomName());
}

И orElseGet() :

@Benchmark
@BenchmarkMode(Mode.AverageTime)
public String orElseGetBenchmark() {
return Optional.of("foreach").orElseGet(() -> getRandomName());
}

При выполнении наших тестовых методов мы получаем:

Benchmark           Mode  Cnt      Score       Error  Units
orElseBenchmark avgt 20 60934.425 ± 15115.599 ns/op
orElseGetBenchmark avgt 20 3.798 ± 0.030 ns/op

Как мы видим, влияние на производительность может быть значительным даже для такого простого сценария использования.

Приведенные выше цифры могут немного отличаться; однако orElseGet() явно превосходит orElse() в нашем конкретном примере.

В конце концов, orElse() включает в себя вычисление метода getRandomName() для каждого запуска.

5. Что важно?

Помимо аспектов производительности, другие факторы, которые стоит учитывать, включают:

  • Что, если метод будет выполнять некоторую дополнительную логику? Например, сделать некоторые вставки или обновления БД
  • Даже когда мы назначаем объект параметру orElse() , мы все равно создаем объект «Другой» без всякой причины :
String name = Optional.of("foreach").orElse("Other")

Вот почему для нас важно сделать тщательный выбор между orElse() и orElseGet() в зависимости от наших потребностей. По умолчанию имеет смысл использовать orElseGet() каждый раз, если только объект по умолчанию уже не создан и не доступен напрямую.

6. Заключение

В этой статье мы узнали о нюансах между методами Optional orElse() и OrElseGet() . Мы также обсудили, как такие простые понятия иногда могут иметь более глубокое значение.

Как всегда, полный исходный код можно найти на Github .