Ответ 1
Я бы сказал, что Guava определенно не сложнее использовать, чем Apache Collections. На самом деле я бы сказал, что это намного проще.
Одна из главных достоинств преимуществ Guava заключается в том, что он не раскрывает так много новых типов объектов... ему нравится хранить большинство реальных типов реализации, которые он использует, скрытно скрываясь за статическими методами factory, которые только открывают интерфейс. Возьмем различные Predicate
s, например. В Apache Collections у вас есть общедоступные классы реализации верхнего уровня, например:
NullPredicate
NotNullPredicate
NotPredicate
AllPredicate
AndPredicate
AnyPredicate
OrPredicate
Плюс больше тонны.
В Guava они аккуратно упакованы в один класс верхнего уровня, Predicates
:
Predicates.isNull()
Predicates.notNull()
Predicates.not(...)
Predicates.and(...)
Predicates.or(...)
Ни один из них не раскрывает их класс реализации, потому что вам не нужно это знать! Хотя Apache Collections имеет эквивалент PredicateUtils
, тот факт, что он предоставляет типы Predicate
, затрудняет его использование. Как я вижу, Apache Collections - всего лишь беспорядок ненужных видимых классов и не очень полезных частей, которые добавляют беспорядок и затрудняют получение и использование полезных частей. Разница очевидна, когда вы смотрите на количество классов и интерфейсов, отображаемых двумя библиотеками:
- Коллекции Apache предоставляют 309 типов.
- Guava, включая все его пакеты (а не только коллекции), содержит только 191 тип.
Добавьте к этому способ, которым Guava гораздо более осторожен, только чтобы включить действительно полезные утилиты и классы, его строгое соблюдение контрактов интерфейсов, которые он реализует, и т.д., и я думаю, что это намного более качественная, простая в использовании библиотека.
Чтобы решить некоторые из ваших конкретных вопросов:
Я на самом деле думаю, что порядок, выбранный Guava для Functions.compose
, более интуитивно понятен (хотя я считаю, что с самого начала субъективный аргумент). Обратите внимание, что в вашем примере композиции с Guava порядок, в котором будут применяться функции, читается с конца объявления в направлении места, где назначается конечный результат. Другая проблема с вашим примером заключается в том, что он не является безопасным для типов, поскольку исходный пример включает в себя передачу результата метода get
другому типу. Преимущество Guava compose
в массиве Transformer
в примере Apache Commons заключается в том, что compose
может выполнять набор функций, безопасный для типов, обеспечивая (во время компиляции), что ряд функций, которые вы применяете, будет работайте правильно. Версия Apache совершенно небезопасна в этом отношении.
Представления превосходят копии:
Во-вторых, о "представлении" в режиме реального времени Collections2.transform
. Чтобы быть тупым, вы совершенно неправы в этом вопросе. Использование живого изображения вместо копирования всех элементов исходного Collection
в новый Collection
на самом деле намного эффективнее! Здесь, что произойдет, когда вы вызовете Collections2.transform
, а затем вызовите contains
на Collection
, он вернется:
- Создается представление
Collection
обертывания оригинала... оригинал иFunction
оба просто назначаются полям в нем. - Итератор
Collection
извлекается. - Для каждого элемента в
Iterator
применяетсяFunction
, получая преобразованное значение этого элемента. - Когда первый элемент, для которого преобразованное значение
equals
найден объект, который вы проверяете, возвращаетсяcontains
. Вы только повторяете (и применяетеFunction
) до тех пор, пока не будет найдено совпадение!Function
применяется не более одного раза на элемент!
Вот что делает версия Apache Collections:
- Создает новый
ArrayList
для хранения преобразованных значений. - Возвращает оригинальный итератор
Collection
. - Для каждого элемента исходного итератора
Collection
применяется эта функция и добавляется результат к новомуCollection
. Это делается для каждого элемента исходногоCollection
, даже если результат примененияTransformer
к самому первому элементу соответствовал бы объекту, который мы ищем! - Затем
contains
будет перебирать каждый элемент новогоCollection
в поисках результата.
Здесь лучшие и худшие сценарии для Collection
размера N, использующего обе библиотеки. Лучшим случаем является преобразование значения первого элемента equals
объекта, который вы ищете с помощью contains
, а наихудший случай - когда значение, которое вы ищете с помощью contains
, не существует в преобразованной коллекции.
- гуавы:
- Лучший случай: итерации 1 элемент, применяется
Function
1 раз, сохраняет 0 дополнительных элементов. - Наихудший случай: выполняет итерацию N элементов, применяет
Function
N раз, сохраняет 0 дополнительных элементов.
- Лучший случай: итерации 1 элемент, применяется
- Apache:
- Лучший случай: итерации N + 1 элементов, применяется
Transformer
N раз, хранит N дополнительных элементов (преобразованная коллекция). - Худший случай: выполняет итерации 2N элементов, применяет
Transformer
N раз, сохраняет N дополнительных элементов (преобразованная коллекция).
- Лучший случай: итерации N + 1 элементов, применяется
Я надеюсь, что из сказанного очевидно, что, в общем, представление - это очень хорошая вещь! Кроме того, очень легко скопировать представление в коллекцию без просмотра в любое время, которое было бы полезно, и это будет иметь ту же производительность, что и версия Apache. Тем не менее, это было бы явно не полезно в любом из приведенных вами примеров.
Как заключительная незначительная заметка, Iterables.contains
существует просто, чтобы вы могли проверить, есть ли Iterable
, который вы не знаете как Collection
, содержит значение. Если Iterable
вы на самом деле это Collection
, он просто вызывает contains()
на этом Collection
, чтобы вы могли обеспечить лучшую производительность (если это Set
, скажем).