Что произошло, когда JAVA перевел функциональный интерфейс на компаратор? [Дубликат]

Одна проблема, которую он решает: Код проще, чем lambda для вызова в конструкторе, который использует функцию выходного параметра для инициализации члена const

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

18
задан Sergey Tachenov 4 January 2016 в 06:58
поделиться

1 ответ

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

Однако сгенерация не является причиной замедления. В конце концов, создание класса с простой структурой может быть даже быстрее, чем загрузка одних и тех же байтов из внешнего источника. И внутренний класс тоже должен быть загружен. Но когда приложение раньше не использовало лямбда-выражения, даже фреймворк для создания лямбда-классов должен быть загружен (текущая реализация Oracle использует ASM под капотом). Это фактическая причина замедления, загрузки и инициализации десятка внутренних классов, а не самого лямбда-выражения.

Вы можете легко проверить это. В вашем текущем коде с использованием лямбда-выражений вы имеете два идентичных выражения (i1, i2) -> Integer.compare(i1.start, i2.start). Текущая реализация не распознает это (фактически, компилятор не дает подсказки ни). Итак, здесь генерируются два экземпляра lambda, имеющие даже разные классы. Вы можете реорганизовать код только с одним компаратором, похожим на ваш внутренний класс:

final Comparator<? super Interval> comparator
  = (i1, i2) -> Integer.compare(i1.start, i2.start);
int start = Collections.binarySearch(intervals, newInterval, comparator);
int skip = start >= 0 ? start : -start - 1;
int end = Collections.binarySearch(intervals.subList(skip, intervals.size()),
                                   new Interval(newInterval.end, 0),
                                   comparator);

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

Вы даже можете увеличить его, вставив дополнительные лямбда-выражения, такие как

final Comparator<? super Interval> comparator1
    = (i1, i2) -> Integer.compare(i1.start, i2.start);
final Comparator<? super Interval> comparator2
    = (i1, i2) -> Integer.compare(i1.start, i2.start);
final Comparator<? super Interval> comparator3
    = (i1, i2) -> Integer.compare(i1.start, i2.start);
final Comparator<? super Interval> comparator4
    = (i1, i2) -> Integer.compare(i1.start, i2.start);
final Comparator<? super Interval> comparator5
    = (i1, i2) -> Integer.compare(i1.start, i2.start);

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

См. Также «Как скомпилировать функции Java лямбда ? " и " Является ли лямбда-выражение созданием объекта в куче каждый раз, когда он выполняется? "

33
ответ дан Community 16 August 2018 в 10:43
поделиться
  • 1
    Любая документация для обеспечения вашего ответа (и помочь нам убедить скептически)? – yunandtidus 6 January 2016 в 21:27
  • 2
    @yunandtidus: Я добавил ссылки на другие вопросы (чьи ответы содержат еще больше ссылок). Тот факт, что ASM используется, является деталью реализации, которая не будет отображаться в спецификации, но ее можно увидеть, посмотрев исходный код – Holger 6 January 2016 в 21:38
  • 3
    Это, возможно, больше, чем первоначально ожидалось OP, но очень интересно, на мой взгляд. Спасибо за указание на это. – yunandtidus 6 January 2016 в 21:42
Другие вопросы по тегам:

Похожие вопросы: