Как красиво получить последний элемент массива?

Когда надо получить последний элемент массива, то как правило используется классическая конструкция:

int a = array[array.length - 1];

Я называю её классической, потому что я встречал её в своих учебных материалах очень часто, и она первая приходит на ум, когда мне надо получить последний элемент массива.

Однако, недавно я увидел, что она порождает проблему: массив может быть пустым и попытка получить последний элемент породит IndexOutOfBoundException. Появляются дополнительные проверки:

int length = array.length;
int a;
if (length != 0) {
    a = array[length - 1];
} else {
    a = 0; // для примера, логика обработки данной ситуации может быть и другой
}

Можно использовать Stream API, и сделать такую конструкцию:

Optional<Integer> a = list.stream().reduce((a, a1) -> a1);

Но она тоже выглядит странно. Почему в List нет метода getLast(), как в LinkedList? Почему в Stream API отсутствует функция last(), тогда как там есть функция first()?

Каковы исторические / технические причины отсутствия метода last() в интерфейсе List и Stream API, и как можно красиво получать последний элемент списка?


Ответы (2 шт):

Автор решения: Nowhere Man

Возможны варианты с использованием тернарного оператора:

// последний элемент непустого массива
public static Optional<Integer> getLast(int ... arr) {
    return null == arr || 0 == arr.length ? Optional.empty() : Optional.of(arr[arr.length - 1]);
}

Почему в List нет метода getLast(), как в LinkedList?
У списков есть ListIterator, для которого можно установить начало итерации в конец списка и вернуть предыдущий элемент ListIterator::previous:

public static Optional<Integer> getLast(List<Integer> list) {
    return null == list || list.isEmpty() ? Optional.empty() : Optional.of(list.listIterator(list.size()).previous());
}

Для списка также есть вариант с использованием Optional::flatMap + Stream::skip:

public static Optional<Integer> getLast(List<Integer> list) {
    return Optional.ofNullable(list).flatMap(in -> in.stream().skip(Math.max(0, in.size() - 1)).findFirst());
}

В последнем случае можно использовать и вариант со Stream::reduce, как наиболее лаконичный (но не производительный):

public static Optional<Integer> getLast(List<Integer> list) {
    return Optional.ofNullable(list).flatMap(in -> in.stream().reduce((a, b) -> b));
}

Ещё один способ: тернарный оператор + Stream::skip:

public static Optional<Integer> getLast(List<Integer> list) {
    return null == list || list.isEmpty() ? Optional.empty() : list.stream().skip(list.size() - 1).findFirst();
}
→ Ссылка
Автор решения: Zhenyria

В Java 21 был внедрён интерфейс SequencedCollection, который призван решить две проблемы:

  1. Слишком общий интерфейс Collection.
  2. Разные способа получения одних и тех же элементов в разных коллекциях.

Теперь у реализаций интерфейса List (который в данный момент наследует интерфейс SequencedCollection), появился метод getLast, который возвращает последний элемент списка:

// java 21 code

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
System.out.println(list.getLast()); // 2 in output
→ Ссылка