Почему интерфейс Java List не поддерживает getLast ()?

18

Я пытаюсь понять несоответствие API в стандартной библиотеке коллекций Java.

В List или в AbstractList нет метода для получения последнего элемента, хотя можно имитировать это с помощью размера и getIndex ().

Однако LinkedList поддерживает эту функцию.

Любая идея, почему было решено не поддерживать этот метод в интерфейсе?

    
задан Uri 26.06.2009 в 16:43
источник
  • После этого несколько раз в коде, я уверен, что именно так, чтобы программисты обслуживания сердились на дизайнера API. :-) Программисты Greenfield в конечном итоге обходят LinkedList (вместо списка), чтобы они могли использовать getLast (). Сторонники обслуживания выясняют, что ArrayList более уместен и должен иметь дело со всеми указанными LinkedLists, которые действительно должны были быть List в первую очередь (и, вероятно, были бы исключены из-за этого критического сбоя в интерфейсе List) ... –  Brian Knoblauch 25.02.2014 в 20:36

7 ответов

19

Интерфейс java.util.List не поддерживает getLast() , потому что дизайнеры пошли на «минимальный интерфейс». С минимальным количеством методов, которые он определяет, он упрощает понимание и ускорение обучения.

Это контрастирует с «гуманным интерфейсом» (например, используется в классе массива Ruby ), который пытается предоставить методы для выполнения общих операций (например, getLast() ). Поскольку существует много применений, которые могут быть связаны с такой фундаментальной концепцией, как список, это приводит к значительно большим интерфейсам.

Для получения дополнительной информации см. Минимальный интерфейс и Humane Interface .

Что касается того, почему LinkedList поддерживает getLast() и т. д., чтобы указать javadoc:

  

... класс LinkedList предоставляет равномерно названные методы для получения, удаления и вставки элемента в начале и конце списка. Эти операции позволяют связанным спискам использоваться в качестве стека, очереди или очереди с двойным завершением (deque).

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

Как понимание ума главного дизайнера API Java Collections (Joshua Bloch), он предоставляет этот список принципов дизайна API , с помощью которых он работает. Из них наиболее уместными в этом вопросе являются:

  

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

     

Если у вас есть сомнения, оставьте это. Если есть фундаментальная теорема проектирования API, то это он. Он в равной степени относится к функциональности, классам, методам и параметрам. Каждый аспект API должен быть как можно меньше, но не меньше. Вы всегда можете добавить вещи позже, но вы не можете их отнять. Минимизация концептуального веса важнее, чем оценка класса или метода.

     

Храните API без сведений о реализации. Они путают пользователей и препятствуют развитию гибкости. Не всегда очевидно, что представляет собой деталь реализации: Будьте осторожны с надстройкой.

     

Минимизировать доступность; когда вы сомневаетесь, сделайте это частным. Это упрощает API и уменьшает сцепление.

     

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

Однако он также заявляет:

  

Не заставляйте клиента делать что-либо, что может сделать библиотека. Нарушение этого правила приводит к заключению шаблона в клиенте, что является раздражающим и подверженным ошибкам.

Что только показывает, что рекомендации по дизайну часто конфликтуют, а самая трудная часть работы дизайнеров API - это сбалансировать эти конфликты.

    
ответ дан Matthew Murdoch 26.06.2009 в 17:15
  • Хорошее объяснение. Заставляет меня задаться вопросом, почему существует метод isEmpty. –  peskal 07.08.2011 в 23:45
  • @peskal С Java 8 мы можем использовать isEmpty в качестве справочника метода, который хорош. –  Thorbjørn Ravn Andersen 21.02.2017 в 12:15
  • @ ThorbjørnRavnAndersen, но он существовал задолго до Java 8;) –  Joffrey 06.02.2018 в 10:16
5

Обычно причина в том, что они хотели указать каждую функцию с требованием Big-O и считали, что getLast () не может быть эффективно реализован во всех списках. Поэтому они представляют его на каждом уровне с обещанием Big-O.

Или это может быть просто недосмотр, или они чувствовали, что это недостаточно распространено, и если вам это нужно, вы можете получить его с размером / getIndex.

    
ответ дан Lou Franco 26.06.2009 в 16:48
  • Мне иногда жаль, что они тоже не добавили его в ArrayList. Написание arraylist.get (arraylist.size () - 1) снова и снова является своего рода шаблоном. –  akarnokd 26.06.2009 в 16:54
  • public class MyArrayList <E> extends ArrayList <E> {public E getLast () {return get (size () - 1);}} –  Charlie 26.06.2009 в 18:35
  • @Charlie: Если вы не работаете с интерфейсом списка и не хотите когда-либо напрямую знать, какова ваша базовая реализация. –  Uri 26.06.2009 в 19:45
  • Я думаю, что Чарли разрешил бы эту проблему, добавив MyList в список List и MyArrayList, чтобы реализовать MyList. –  akarnokd 26.06.2009 в 21:32
  • @akarnokd Тогда вам придется изменить весь свой код и заставить читателей задаться вопросом, почему пользовательский список, все это только для удобства getLast (). Мне тоже кажется, что это не в интерфейсе списка, я понимаю минималистский подход, но иногда вы получаете много, просто добавляя крошечную помощь, например isEmpty (). –  Joffrey 06.02.2018 в 10:19
2

Цель интерфейса - обеспечить максимальное удобство использования с минимально возможным количеством общедоступных методов. Чем меньше методов поддержки, тем лучше. Нет метода getLast (), потому что это может быть выведено, как вы сказали.

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

Изменить: как указал Скаффман, это не главная цель main интерфейса. Это скорее вторичная цель, которая позволяет упростить реализацию этого интерфейса. Основная цель интерфейса - отделить конкретную реализацию от объектов, которые ее используют.

    
ответ дан Zack Marrapese 26.06.2009 в 16:47
  • Нет, цель интерфейса - отделить реализацию от интерфейса. Тип операций не имеет к этому никакого отношения. –  skaffman 26.06.2009 в 16:50
  • Я не уверен, что согласен, но я думаю, это зависит от того, насколько вероятно, что кто-то хочет, чтобы последний элемент из списка. –  Uri 26.06.2009 в 16:53
  • Интересно, какие другие реализации списков возможны помимо встроенного LinkedList / ArrayList в том, что общий метод getFirst () getLast () не был O (1)? –  akarnokd 26.06.2009 в 16:56
  • @ skaffman: Ты прав. Конечно, это основная цель интерфейса. Я говорю больше с точки зрения вторичной цели. Я уточню свой ответ, чтобы отразить это. –  Zack Marrapese 26.06.2009 в 21:25
  • С появлением методов по умолчанию для Java 8 вы можете иметь лучшее из обоих миров, предоставляя приятные методы для пользователей интерфейса, не заставляя разработчиков писать их сами. Таким образом, они могли бы добавить T getLast () по умолчанию, и все были бы счастливы. –  Joffrey 06.02.2018 в 10:22
1

Как вы сказали, с помощью List вы можете получить последний с getIndex (), поскольку он основан на индексе. Поэтому лично я не вижу веских оснований для этого (поскольку вы можете написать его сами).

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

    
ответ дан Alexandru Luchian 26.06.2009 в 16:48
1

Метод getLast () - это интерфейс Deque, который реализует LinkedList. Если вы хотите получить версию с поддержкой массива, вы можете использовать ArrayDeque. Я полагаю, что это не часть интерфейса List, потому что они хотели отделить разные абстрактные типы данных от отдельных интерфейсов, хотя реализация, такая как LinkedList, может реализовать более одного интерфейса.

    
ответ дан Daire 26.06.2009 в 17:16
  • Интерфейс Deque был введен только в Java 6. Класс LinkedList (с его методом getLast ()) предшествует нескольким версиям (представлен в 1.2). –  Matthew Murdoch 26.06.2009 в 19:09
0

getLast () не может быть реализован в одностороннем связанном списке. getIndex () в одностороннем связанном списке, вероятно, сканирует весь список с самого начала, чтобы он имел эффективность.

    
ответ дан clemahieu 26.06.2009 в 17:03
  • Если только он не сохранил последний элемент в поле. Как вы думаете, реализована ли функция getSize ()? –  Michael Myers♦ 26.06.2009 в 17:09
  • Именно поэтому ссылка LinkedList Java связана с двойной связью или в одном связанном случае, список просто содержит ссылку на свой последний элемент, чтобы добавить дополнение O (1) в конце списка. –  akarnokd 26.06.2009 в 17:20
0

В интерфейсе List нет каких-либо удобных методов такого типа. Нет getFirst () или getLast (). Он строго основан на индексе. LinkedList нуждается в способе избегать поиска по индексу, если это возможно по причинам производительности, поэтому getLast - это действительно оптимизация, а не метод удобства.

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

    
ответ дан Yishai 26.06.2009 в 17:16
  • Если вы посмотрите на реализацию LinkedList, она содержит оптимизацию для блокировки на основе индекса: если индекс равен половине размера списка, тогда обход начинается в конце и идет назад. Таким образом, get (размер - 1) практически имеет такую ​​же стоимость времени, что и getLast (). –  akarnokd 26.06.2009 в 17:23
  • @ kd304, то есть деталь реализации, которая не затрагивает общий случай поиска по индексу, который не является оптимальным для LinkedList. В LinkedList нет контракта, согласно которому определенные индексированные поисковые запросы будут быстрыми. –  Yishai 26.06.2009 в 17:32