Java: есть ли функция карты?

141

Мне нужна функция карты . Есть ли что-то подобное в Java?

(Для тех, кто задается вопросом: я, конечно, знаю, как реализовать эту тривиальную функцию сам ...)

Альберт
источник
1
Если нет, то определить себя просто. Но я полагаю, что Google знает дюжину реализаций?
2
Дублировано (скорее лучше) на stackoverflow.com/questions/3907412/…
Chowlett
6
@Chris: Как это тот же вопрос?
Альберт
1
Если ответ на этот вопрос положительный, он также отвечает на другой связанный вопрос. Если ответ отрицательный (а так кажется), они совершенно не связаны.
Альберт
1
Что касается Java8, это то, что @delnan мог иметь в виду на leveluplunch.com/java/examples/…
Eternalcode

Ответы:

86

В java 6 нет понятия функции в JDK.

Однако у Guava есть функциональный интерфейс, и метод обеспечивает необходимую вам функциональность.
Collections2.transform(Collection<E>, Function<E,E2>)

Пример:

// example, converts a collection of integers to their
// hexadecimal string representations
final Collection<Integer> input = Arrays.asList(10, 20, 30, 40, 50);
final Collection<String> output =
    Collections2.transform(input, new Function<Integer, String>(){

        @Override
        public String apply(final Integer input){
            return Integer.toHexString(input.intValue());
        }
    });
System.out.println(output);

Выход:

[a, 14, 1e, 28, 32]

В наши дни, в Java 8, на самом деле есть функция карты, поэтому я бы, вероятно, написал код более кратко:

Collection<String> hex = input.stream()
                              .map(Integer::toHexString)
                              .collect(Collectors::toList);
Шон Патрик Флойд
источник
8
Стоит отметить, что хотя с Guava вы можете это сделать, возможно, вам не захочется: code.google.com/p/guava-libraries/wiki/FunctionalExplained (прочтите раздел «Предостережения»).
Адам Паркин
2
@AdamParkin правда, но я почти уверен, что это относится к более продвинутым функциональным концепциям, чем это, иначе они бы не разработали методы transform ( ) в первую очередь,
Шон Патрик Флойд
2
На самом деле, нет, функциональные идиомы часто оказывают определенное снижение производительности, поэтому они подчеркивают, что вы должны использовать средства только в том случае, если вы уверены, что они соответствуют двум указанным критериям: чистая экономия LOC для кодовой базы в целом и доказанная прирост производительности из-за ленивой оценки (или, по крайней мере, отсутствия ударов по производительности). Не возражая против их использования, просто указывая, что если вы собираетесь это сделать, вам следует прислушаться к предупреждениям разработчиков.
Адам Паркин
4
@SeanPatrickFloyd теперь, когда вышла Java 8, хотите обновить это примером с лямбдами? НравитсяCollections2.transform(input -> Integer.toHexString(intput.intValue())
Любаров Даниил 04
2
@Daniel В Java 8 я не вижу причин делать это с Guava. Вместо этого я бы пошел на ответ Левентова
Шон Патрик Флойд
94

Начиная с Java 8, в JDK есть несколько стандартных опций:

Collection<E> in = ...
Object[] mapped = in.stream().map(e -> doMap(e)).toArray();
// or
List<E> mapped = in.stream().map(e -> doMap(e)).collect(Collectors.toList());

Смотрите java.util.Collection.stream()и java.util.stream.Collectors.toList().

Левентов
источник
143
Это так многословно, что мне больно внутри.
Natix
1
@Natix согласен toList(). Замена на другой тип:(List<R>)((List) list).replaceAll(o -> doMap((E) o));
leventov
2
Можно e -> doMap(e)заменить просто doMap?
jameshfisher
3
@jameshfisher, да, что-то вроде foo::doMapили Foo::doMap.
левентов
9
Думаю, именно поэтому существует Scala. Подождите, пока в Java 12 появится что-нибудь читаемое.
JulienD 02
26

Существует замечательная библиотека под названием Functional Java, которая обрабатывает многие вещи, которые вы хотели бы иметь в Java, но это не так. Опять же, есть еще этот замечательный язык Scala, который делает все, что должна была делать Java, но не делает, но при этом остается совместимым с чем-либо, написанным для JVM.

пшеница
источник
Меня интересует, как они включили следующий синтаксис: a.map({int i => i + 42});расширили ли они компилятор? или добавлен препроцессор?
Андрей
@Andrey - Вы можете спросить их об этом сами или посмотреть исходный код, чтобы увидеть, как это делается. Вот ссылка на источник: functionjava.org/source
Wheaties
1
@Andrey: в примерах используется синтаксис из предложения по закрытию BGGA. Пока есть работающий прототип, он еще не в «официальной» Java.
Петер Штибрани
@Andrey: этот синтаксис является частью предлагаемой спецификации замыканий в Java (см. Предпоследний абзац на домашней странице). Есть только прототипная реализация.
Майкл Боргвардт,
2
Перехват потока Scala: (Надеюсь, SO не станет похожим на список рассылки JavaPosse;)
Йорн
9

Будьте очень осторожны с Collections2.transform()гуавой. Самым большим преимуществом этого метода является его самая большая опасность: его лень.

Посмотрите документацию Lists.transform(), которая, как мне кажется, применима также к Collections2.transform():

Функция применяется лениво, вызывается при необходимости. Это необходимо для того, чтобы возвращаемый список был представлением, но это означает, что функция будет применяться много раз для массовых операций, таких как List.contains (java.lang.Object) и List.hashCode (). Чтобы это работало хорошо, функция должна быть быстрой. Чтобы избежать ленивой оценки, когда возвращаемый список не обязательно должен быть представлением, скопируйте возвращенный список в новый список по вашему выбору.

Также в документации Collections2.transform()упоминается, что вы получаете представление в реальном времени, что изменение в исходном списке влияет на преобразованный список. Такое поведение может привести к проблемам, которые трудно отследить, если разработчик не понимает, как оно работает.

Если вам нужна более классическая «карта», которая будет запускаться только один раз, тогда вам лучше использовать FluentIterableGuava, который имеет гораздо более простую операцию. Вот пример этого в Google:

FluentIterable
       .from(database.getClientList())
       .filter(activeInLastMonth())
       .transform(Functions.toStringFunction())
       .limit(10)
       .toList();

transform()вот метод карты. Он использует те же "обратные вызовы" Function <>, что и Collections.transform(). Список, который вы получите, доступен только для чтения, используйте его copyInto()для получения списка для чтения и записи.

В противном случае, конечно, когда java8 выйдет с лямбдами, это будет устаревшим.

Эммануэль Тузери
источник
2

Хотя это старый вопрос, я хотел бы показать другое решение:

Просто определите свою собственную операцию, используя java generics и java 8 streams:

public static <S, T> List<T> map(Collection<S> collection, Function<S, T> mapFunction) {
   return collection.stream().map(mapFunction).collect(Collectors.toList());
}

Тогда вы можете написать такой код:

List<String> hex = map(Arrays.asList(10, 20, 30, 40, 50), Integer::toHexString);
IPP Nerd
источник