Страницы

Поиск по вопросам

воскресенье, 26 января 2020 г.

Зачем в Java 9 добавили List.of

#java #java_9


Зачем в Java 9 добавили List.of с несколькими сигнатурами?

Например, есть:


 List of(E e1, E e2, E e3)
 List of(E e1, E e2)
 List of(E e1)
...
 List of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10)


При этом в той же Java 9 сразу добавили:
 List of(E... elements),
который заменяет все предыдущие методы. Тогда в чем их смысл?
    


Ответы

Ответ 1



JEP 269: These will include varargs overloads, so that there is no fixed limit on the collection size. However, the collection instances so created may be tuned for smaller sizes. Special-case APIs (fixed-argument overloads) for up to ten of elements will be provided. While this introduces some clutter in the API, it avoids array allocation, initialization, and garbage collection overhead that is incurred by varargs calls. Significantly, the source code of the call site is the same regardless of whether a fixed-arg or varargs overload is called. Джошуа Блох - Java Эффективное программирование (статья 42): Нужно быть осторожными при использовании возможностей varargs в ситуациях, где критична производительность. Каждый запуск метода с переменным числом параметров приводит к созданию и инициализации массива. Если вы понимаете, что не можете позволить себе такие расходы, но вам нужна гибкость методов varargs, есть шаблон, который позволит совместить преимущества обоих подходов. Предположим, вы определили, что 95% обращений к методу содержит 3 и менее параметров. Тогда объявим 5 перегрузок метода, каждый с количеством обычных параметров от нуля до трёх, и один метод varargs для использования, когда количество аргументов превысит 3: public void foo() { } public void foo(int a1) { } public void foo(int a1, int a2) { } public void foo(int a1, int a2, int a3) { } public void foo(int a1, int a2, int a3, int... rest) { } Теперь вы знаете, что затраты на создание массивов составят лишь 5% от всех вызовов в случаях, где количество параметров будет больше трёх.

Ответ 2



Для оптимизации. Тут важно отметить, что возвращаемые такими методами коллекции являются неизменяемыми. Т.е. мы можем сделать класс, который имеет два поля (первый и второй элемент) вместо массива (это аж 8 байт памяти) или другой структуры, и в этих полях держать значения. Соответственно при запросе размера коллекции мы можем сразу вернуть константу 2, а не искать это значение через дополнительные операции. И так далее. Собственно вот пример из сырцов OpenJdk List класса для List of(E e1, E e2) static final class List2 extends AbstractImmutableList { @Stable private final E e0; @Stable private final E e1; List2(E e0, E e1) { this.e0 = Objects.requireNonNull(e0); this.e1 = Objects.requireNonNull(e1); } @Override public int size() { return 2; } @Override public E get(int index) { Objects.checkIndex(index, 2); if (index == 0) { return e0; } else { // index == 1 return e1; } } @Override public boolean contains(Object o) { return o.equals(e0) || o.equals(e1); // implicit nullcheck of o } @Override public int hashCode() { int hash = 31 + e0.hashCode(); return 31 * hash + e1.hashCode(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException("not serial proxy"); } private Object writeReplace() { return new CollSer(CollSer.IMM_LIST, e0, e1); } } P.S. Могу добавить интересный аргумент. К примеру во всем известной IntelliJ Idea есть оптимизированная реализация для List, у которого всего один элемент. И даже есть видео, где один из разработчиков объясняет, что таким образом они стремятся сэкономить память.

Комментариев нет:

Отправить комментарий