Java: чи є функція карти?


140

Мені потрібна функція карти . Чи є щось подібне в Java вже?

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


1
Якщо ні, то банально визначати себе. Але я припускаю, що Google знає десяток реалізацій?

2
Дубльований (а краще) в stackoverflow.com/questions/3907412 / ...
Chowlett

6
@Chris: Як це те саме питання?
Альберт

1
Якщо відповідь на це питання так, він відповідає також на інше пов'язане питання. Якщо відповідь "ні" (і здається, що так), вони абсолютно не пов'язані між собою.
Альберт

1
Що стосується Java8, це те, що @delnan, можливо, посилалося на leveluplunch.com/java/examples/…
Eternalcode

Відповіді:


86

Поняття функції в JDK не існує як java 6.

У 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 (читайте розділ "Caveats").
Адам Паркін

2
@AdamParkin правда, але я впевнений, що це стосується більш прогресивних функціональних концепцій, ніж це, інакше вони б не розробили в першу чергу методи трансформації ( )
Шон Патрік Флойд

2
Насправді, ні, часто є певний показник ефективності функціональних ідіом, і тому вони наголошують, що ви повинні використовувати засоби лише у тому випадку, якщо ви впевнені, що це відповідає двом окресленим критеріям: чиста економія LOC для кодової бази в цілому та доведена підвищення ефективності завдяки лінивій оцінці (або, принаймні, не ефективності). Не сперечаючись проти їх використання, просто вказуючи на те, що якщо ви збираєтеся, ви повинні прислухатися до попереджень реалізаторів.
Адам Паркін

4
@SeanPatrickFloyd тепер, коли Java 8 не працює, хочете оновити це на прикладі, що включає лямбда? ЯкCollections2.transform(input -> Integer.toHexString(intput.intValue())
Даніель Любаров

2
@Daniel У Java 8 я не бачу причини робити це з Guava. Натомість я б пішов на відповідь Левентова
Шон Патрік Флойд

92

Оскільки 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().


140
Це стільки багатослівного, що болить мені всередині.
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
Я думаю, саме тому Скала існує. Зачекайте, коли у Java 12 з’явиться щось читабельне.
JulienD

26

Є чудова бібліотека під назвою Функціональна Java, яка обробляє багато речей, які ви хочете мати у Java, але це не так. Знову ж таки, є також ця чудова мова Scala, яка робить все, що повинно було зробити Java, але не є сумісною ні з чим, написаним для JVM.


Мене цікавить, як вони включили наступний синтаксис: a.map({int i => i + 42});вони розширили компілятор? чи доданий препроцесор?
Андрій

@Andrey - Ви можете запитати їх самі або перевірити вихідний код, щоб побачити, як це робиться. Ось посилання на джерело: functionaljava.org/source
Wheaties

1
@Andrey: у прикладах використовується синтаксис пропозиції BGGA про закриття. Поки існує прототип, він ще не є в "офіційній" Java.
Петро Штібрані

@Andrey: цей синтаксис є частиною запропонованої специфікації для закриття Java (див. Другий абзац на головній сторінці). Є лише прототипова реалізація.
Майкл Боргвардт

2
Викрадення теми Scala :( Я сподіваюся, що SO не стане подібним до списку розсилки JavaPosse;)
Jorn,

9

Будьте дуже обережні з Collections2.transform()гуавою. Найбільшою перевагою цього методу є також його найбільша небезпека: його лінь.

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

Функція застосовується ліниво, викликається при необхідності. Це необхідно, щоб повернутий список був переглядом, але це означає, що функція буде застосовуватися багато разів для масових операцій, таких як List.contains (java.lang.Object) та List.hashCode (). Щоб це було добре, функція повинна бути швидкою. Щоб уникнути лінивої оцінки, коли повернений список не потребує перегляду, скопіюйте повернений список у новий список, який ви обрали.

Також у документації Collections2.transform()згаданих вами ви отримуєте перегляд у прямому ефірі, що зміни у списку джерел впливають на перетворений список. Така поведінка може призвести до важко відстежувати проблеми, якщо розробник не усвідомлює, як це працює.

Якщо ви хочете отримати більш класичну "карту", яка буде працювати лише один раз і один раз, тоді вам краще скористатися FluentIterable, також від Guava, яка має операцію, яка набагато простіша. Ось приклад google для цього:

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

transform()ось метод карти. Він використовує ту ж функцію <> "зворотні дзвінки", що і Collections.transform(). Список, який ви отримуєте назад, доступний лише для читання, використовуйте copyInto()для отримання списку читання-запису.

Інакше, звичайно, коли java8 вийде з лямбдами, це буде застарілим.



2

Хоча це старе питання, я хотів би показати інше рішення:

Просто визначте власну операцію за допомогою Java generics та потоків java 8:

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);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.