Вопрос-ответ

Type List vs type ArrayList in Java [duplicate]

Список типов против типа ArrayList в Java
(1) List<?> myList = new ArrayList<?>();

(2) ArrayList<?> myList = new ArrayList<?>();

Я понимаю, что с помощью (1) реализации интерфейса List можно поменять местами. Кажется, что (1) обычно используется в приложении независимо от необходимости (сам я всегда использую это).

Мне интересно, использует ли кто-нибудь (2)?

Кроме того, как часто (и могу ли я, пожалуйста, привести пример) ситуация на самом деле требует использования (1) вместо (2) (т. Е. Там, где (2) было бы недостаточно .. помимо кодирования интерфейсов и лучших практик и т.д.)

Переведено автоматически
Ответ 1

Почти всегда List предпочтительнее, чем ArrayList потому что, например, List может быть переведен в LinkedList, не затрагивая остальную кодовую базу.

Если использовать ArrayList вместо List, то трудно заменить ArrayList реализацию на LinkedList другую, потому что ArrayList в кодовой базе использовались определенные методы, которые также потребовали бы реструктуризации.

Вы можете прочитать о List реализациях здесь.

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

Ответ 2

Мне интересно, использует ли кто-нибудь (2)?


ДА. Но редко по уважительной причине (IMO).

И люди обжигаются, потому что они использовали ArrayList когда они должны были использовать List:


  • Служебные методы, такие как Collections.singletonList(...) или Arrays.asList(...), не возвращают ArrayList.



  • Методы в List API не гарантируют возврата списка того же типа.



Например, кто-то обжегся, в https://javalang.ru/a/1481123/139985 у постера были проблемы с "нарезкой", потому что ArrayList.sublist(...) не возвращает ArrayList ... и он разработал свой код для использования ArrayList в качестве типа всех своих переменных списка. В итоге он "решил" проблему, скопировав подсписок в новый ArrayList.

Аргумент о том, что вам нужно знать, как List ведет себя, в основном решается с помощью RandomAccess интерфейса маркера. Да, это немного неуклюже, но альтернатива хуже.


Кроме того, как часто ситуация на самом деле требует использования (1) вместо (2) (т. Е. Там, где (2) было бы недостаточно .. помимо "кодирования интерфейсов" и лучших практик и т.д.)


Часть вопроса "как часто" объективно не имеет ответа.


(и могу ли я, пожалуйста, привести пример)


Иногда приложению может потребоваться, чтобы вы использовали методы в ArrayList API, которых нет в List API. Например, ensureCapacity(int), trimToSize() или removeRange(int, int). (И последний вариант возникнет только в том случае, если вы создали подтип ArrayList, который объявляет метод равным public.)

Это единственная разумная причина для кодирования класса, а не интерфейса, IMO.

(Теоретически возможно, что вы получите небольшое улучшение производительности ... при определенных обстоятельствах ... на некоторых платформах ... но если вам действительно не нужны последние 0,05%, не стоит этого делать. Это не веская причина, ИМО.)



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


Это верный аргумент. Однако Java предоставляет лучшие способы справиться с этим; например

public <T extends List & RandomAccess> void test(T list) {
// do stuff
}

Если вы вызовете это со списком, который не реализуется RandomAccess вы получите ошибку компиляции.

Вы также можете тестировать динамически ... используя instanceof ... если статическая типизация слишком неудобна. И вы могли бы даже написать свой код для использования разных алгоритмов (динамически) в зависимости от того, поддерживает ли список произвольный доступ.

Обратите внимание, что ArrayList это не единственный класс list, который реализует RandomAccess. Другие включают CopyOnWriteList, Stack и Vector.

Я видел, как люди приводили тот же аргумент по поводу Serializable (потому что List это не реализует) ... но описанный выше подход решает и эту проблему. (В той степени, в какой это вообще решаемо с использованием типов среды выполнения. В ArrayList произойдет сбой сериализации, если какой-либо элемент не является сериализуемым.)


Наконец, я не собираюсь говорить "потому что это хороший стиль". Эта "причина" является как циклическим аргументом ("Почему это "хороший стиль"?"), так и ссылкой на неустановленный (и, вероятно, несуществующий!) более высокий авторитет ("Кто сказал, что это "хороший стиль"?").

(Я действительно думаю, что это хороший стиль программирования для интерфейса, но я не собираюсь приводить это в качестве причины. Вам лучше понять реальные причины и прийти к (IMO) правильным выводам для себя. Правильный вывод может не всегда совпадать ... в зависимости от контекста.)

Ответ 3

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

Использование:

List list = new ArrayList(100); // will be better also to set the initial capacity of a collection 

Вместо:

ArrayList list = new ArrayList();

Для справки:

введите описание изображения здесь

(опубликовано в основном для диаграммы коллекции)

Ответ 4

Считается хорошим стилем хранить ссылку на HashSet or TreeSet в переменной типа Set.

Set<String> names = new HashSet<String>();

Таким образом, вам придется изменить только одну строку, если вы решите использовать TreeSet вместо этого.

Кроме того, методы, которые работают с наборами, должны указывать параметры типа Set:

public static void print(Set<String> s)

Затем этот метод можно использовать для всех заданных реализаций.

Теоретически, мы должны использовать ту же рекомендацию для связанных списков, а именно сохранять ссылки на LinkedList в переменных типа List . Однако в библиотеке Java интерфейс List является общим как для ArrayList, так и для LinkedList класса. В частности, в нем есть методы get и set для произвольного доступа, хотя эти методы очень неэффективны для связанных списков.

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

Это явно серьезная ошибка проектирования в стандартной библиотеке, и по этой причине я не могу рекомендовать использовать интерфейс List.

Чтобы увидеть, насколько неприятна эта ошибка, взгляните на исходный код для binarySearch метода класса Collections. Этот метод принимает параметр List, но бинарный поиск не имеет смысла для связанного списка. Затем код неуклюже пытается определить, является ли список связанным списком, а затем переключается на линейный поиск!

Set Интерфейс и Map interface, хорошо разработаны, и вы должны их использовать.

java list