Производительность thenComparing против thenComparingInt — что использовать?

#sorting #java-8 #comparator

#сортировка #java-8 #компаратор

Вопрос:

У меня есть вопрос, если я сравниваю целые числа, есть ли разница в производительности при вызове thenComparingInt(My::intMethod) против thenComparing(My::intMethod), другими словами, если я сравниваю разные типы, как ссылочные, так и примитивные, например, String, int и т. Д. Часть меня просто хочет сказать comparing().thenComparing().thenComparing() и т. Д., Но должен ли я делать comparing.thenComparing().thenComparingInt(), если 3-й вызов сравнивал значение int или целое число?

Я предполагаю, что comparing() и thenComparing() используют метод compareTo для сравнения любого заданного типа за кулисами или, возможно, для целых чисел, Integer.compare? Я также предполагаю, что ответ на мой первоначальный вопрос может включать производительность в том смысле, что thenComparingInt будет знать, что передается int , тогда как thenComparing должен был бы автоматически преобразовывать int в Integer, а затем, возможно, приводить к Object?

Кроме того, еще один вопрос, пока я думаю об этом — есть ли способ объединения ссылок на методы, например, Song::getArtist::length, где getArtist возвращает строку? Причина в том, что я хотел сделать что-то вроде этого:

 songlist.sort(
            Comparator.comparing((Song s) -> s.getArtist().length()));


    songlist.sort(
            Comparator.comparing(Song::getArtist, 
                    Comparator.comparingInt(String::length)));


    songlist.sort(
            Comparator.comparing(Song::getArtist, String::length));
 

Из 3 примеров два верхних компилируются, но нижний, похоже, выдает ошибку компиляции в Eclipse, я бы подумал, что 2-й аргумент String::length был действительным? Но, может быть, не так, поскольку он ожидает, что компаратор не является функцией?

Ответ №1:

Вопрос 1

Я бы подумал thenComparingInt(My::intMethod) , что это может быть лучше, поскольку в нем следует избегать бокса, но вам придется попробовать обе версии, чтобы увидеть, действительно ли это имеет значение.

Вопрос 2

 songlist.sort(
        Comparator.comparing(Song::getArtist, String::length));
 

Недопустимо, потому что 2-й параметр должен быть a Comparator , а не методом, который возвращает int.

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

1. Я предполагаю, что при примитивной специализации будет получена очень небольшая производительность. У Escape-анализа не будет особых проблем с обнаружением того, что автоматически загруженный int ограничен областью действия метода сравнения, и тогда Integer.compareTo метод может быть встроен для получения почти того же результата, что и без автоматической упаковки. Поэтому для поиска фактической разницы необходим сравнительный анализ.

2. @MarkoTopolnik Если бы только это было так. Было сделано много фактических измерений, прежде чем мы взяли на себя обязательство включить примитивные специализации потоков. Тем не менее, производительность редко является самым важным требованием; если у вас в коллекции шесть элементов, сортировка, вероятно, выполняется достаточно быстро, независимо от того, сколько пакетов вы выполняете, а fast enough — это достаточно быстро.

3. @BrianGoetz Действительно, я Arrays.sort(integerArray, comparing(Integer::intValue)); сравнил Arrays.sort(integerArray, comparingInt(Integer::intValue)); , и соотношение производительности составило 3,5 к 1 в пользу примитивной специализации при использовании целых чисел за пределами диапазона кэша целых чисел; когда в пределах диапазона кэша (0 ..127) производительность коробочного варианта была лучше (2 к 1). Это указывает на то, что анализ экранирования не позволил выделить стек, основываясь на моем предыдущем наблюдении, что целые числа EA’d работают лучше , чем при поиске из кэша.

4. @BrianGoetz … следуя приведенному выше выводу, я также протестировал Arrays.sort(integerArray, comparing(i->new Integer(i.intValue()))); — и, о чудо, в этом случае соотношение было всего 1,5 к 1. Я упустил из виду, что Integer.valueOf (другими словами, автоматически загруженный код) не может быть проверен из-за пути кода, включающего поиск в кэше. Итак, мое предсказание все-таки выполняется: при действии EA разница становится совсем небольшой.

5. @MarkoTopolnik Я согласен со всем, что говорит Брайан Гетц, учитывая, что он один из авторов Java 8.