В этом посте приводится пример использования метода Collections2.transform() из библиотеки Google Guava.
Недавно я столкнулся с типичной ситуацией, когда имея коллекцию объектов мне понадобилось получить коллекцию, в которой бы содержались не сами объекты, а их идентификаторы. Сделать это просто: создать коллекцию, проитерироваться по исходной, вызвать метод getId() у каждого элемента и его результат сохранить в новую коллекцию.
Но есть ещё один способ сделать это. Если я не ошибаюсь, этот подход называют функциональным. В частности мы можем объявить метод, в котором инкапсулируем нашу логику (извлечение идентификатора) и вызвать метод transform(), которому передать наш исходный массив и наш метод. transform() сам сделает обход коллекции и вызовет наш метод для каждого элемента.
Какие плюсы у этого подхода? (обратите внимание, что я говорю не про конкретную реализацию, а про сам подход.)
Вот пример «в лоб», который иллюстрирует функциональный подход с использованием Collections2.transform():
В результате программа выведет
Первым аргументом transform() принимает исходную коллекцию, а вторым функтор (класс реализующий интерфейс Function) Нам необходимо реализовать метод apply() которому в качестве аргумента передаётся наша сущность, а он возвращает её идентификатор.
Несмотря на то, что локальный класс достаточно прост я всё же рекомендую вынести его в отдельную переменную. Во-первых, это повысит читаемость, т.к. переменной можно дать говорящее имя. Во-вторых, эту переменную можно использовать ещё раз.
Пример:
Теперь важное уточнение о том, как это работает, а вернее, реализовано. Вопреки вашим ожиданием, и в соответствии с документацией, transform() возвращает не новую коллекцию, а отображение существующей (live view). Это важная деталь, которую нужно иметь ввиду. В действительности это означает что:
Недавно я столкнулся с типичной ситуацией, когда имея коллекцию объектов мне понадобилось получить коллекцию, в которой бы содержались не сами объекты, а их идентификаторы. Сделать это просто: создать коллекцию, проитерироваться по исходной, вызвать метод getId() у каждого элемента и его результат сохранить в новую коллекцию.
Но есть ещё один способ сделать это. Если я не ошибаюсь, этот подход называют функциональным. В частности мы можем объявить метод, в котором инкапсулируем нашу логику (извлечение идентификатора) и вызвать метод transform(), которому передать наш исходный массив и наш метод. transform() сам сделает обход коллекции и вызовет наш метод для каждого элемента.
Какие плюсы у этого подхода? (обратите внимание, что я говорю не про конкретную реализацию, а про сам подход.)
- Инкапсуляция алгоритма
- Как следствие первого пункта повышается возможность этот код использовать повторно
- Позволяет распараллелиться при обработке данных (использование цикла вынуждает нас быть последовательными, в случае же использования transform() у нас такого ограничения нет)
Вот пример «в лоб», который иллюстрирует функциональный подход с использованием Collections2.transform():
В результате программа выведет
[1, 2, 3]
Первым аргументом transform() принимает исходную коллекцию, а вторым функтор (класс реализующий интерфейс Function) Нам необходимо реализовать метод apply() которому в качестве аргумента передаётся наша сущность, а он возвращает её идентификатор.
Несмотря на то, что локальный класс достаточно прост я всё же рекомендую вынести его в отдельную переменную. Во-первых, это повысит читаемость, т.к. переменной можно дать говорящее имя. Во-вторых, эту переменную можно использовать ещё раз.
Пример:
Теперь важное уточнение о том, как это работает, а вернее, реализовано. Вопреки вашим ожиданием, и в соответствии с документацией, transform() возвращает не новую коллекцию, а отображение существующей (live view). Это важная деталь, которую нужно иметь ввиду. В действительности это означает что:
- результат вычисляется тогда, когда вы к нему обратитесь (и даже более того: при каждом обращении он будет вычисляться снова и снова, поэтому если вам нужно использовать результат несколько раз, то лучше его закэшировать)
- изменения в исходной коллекции будут влиять на результат (иными словами результат вызова transform() до и после добавления нового элемента в исходную коллекцию будет отличаться)
- при попытке добавить элемент в результирующую коллекцию вы получите UnsupportedOperationException (т.е. добавить вы ничего не сможете, а вот удалить — запросто)
В Guava также есть Iterables.transform() и Lists.transform(), которые можно применять к соответствующим типам.
Также имеется несколько готовых реализаций интерфейса Function в классе Functions. Например:
- constant() всегда возвращает определённое значение
- toStringFunction вызывает toString() на каждом аргументе
- compose() позволяет объединять в цепочку несколько функторов
Комментариев нет:
Отправить комментарий