Как красиво получить последний элемент массива?
Когда надо получить последний элемент массива, то как правило используется классическая конструкция:
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 шт):
Возможны варианты с использованием тернарного оператора:
// последний элемент непустого массива
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();
}
В Java 21 был внедрён интерфейс SequencedCollection, который призван решить две проблемы:
- Слишком общий интерфейс
Collection. - Разные способа получения одних и тех же элементов в разных коллекциях.
Теперь у реализаций интерфейса 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