Java: czy jest funkcja mapy?

140

Potrzebuję funkcji mapy . Czy jest już coś takiego w Javie?

(Dla tych, którzy się zastanawiają: oczywiście wiem, jak sam zaimplementować tę banalną funkcję ...)

Albert
źródło
1
Jeśli nie, zdefiniowanie siebie jest trywialne. Ale przypuszczam, że Google zna kilkanaście wdrożeń?
2
Zduplikowane (raczej lepsze) na stackoverflow.com/questions/3907412/ ...
Chowlett,
6
@Chris: Jak to jest to samo pytanie?
Albert,
1
Jeśli odpowiedź na to pytanie brzmi „tak”, oznacza to również odpowiedź na inne powiązane pytanie. Jeśli odpowiedź brzmi nie (i tak się wydaje), to są one całkowicie niezwiązane.
Albert,
1
Od wersji Java8 to właśnie @delnan mógł odnosić się do leveluplunch.com/java/examples/ ...
Eternalcode

Odpowiedzi:

86

W JDK nie ma pojęcia funkcji od wersji java 6.

Guava ma jednak interfejs funkcji , a metoda zapewnia wymaganą funkcjonalność.
Collections2.transform(Collection<E>, Function<E,E2>)

Przykład:

// 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);

Wynik:

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

W dzisiejszych czasach, w Javie 8, istnieje funkcja mapy, więc prawdopodobnie napisałbym kod w bardziej zwięzły sposób:

Collection<String> hex = input.stream()
                              .map(Integer::toHexString)
                              .collect(Collectors::toList);
Sean Patrick Floyd
źródło
8
Warto zauważyć, że chociaż z Guava możesz to zrobić, możesz nie chcieć: code.google.com/p/guava-libraries/wiki/FunctionalExplained (przeczytaj sekcję „Ostrzeżenia”).
Adam Parkin,
2
@AdamParkin to prawda, ale jestem prawie pewien, że odnosi się to do bardziej zaawansowanych koncepcji funkcjonalnych niż ta, w przeciwnym razie nie opracowaliby metod transform ( ) w pierwszej kolejności
Sean Patrick Floyd
2
Właściwie nie, idiomy funkcjonalne często mają wyraźny wpływ na wydajność, dlatego podkreślają, że powinieneś używać tych udogodnień tylko wtedy, gdy masz pewność, że spełniają one dwa wymienione kryteria: oszczędności netto LOC dla całej bazy kodu i udowodnione wzrost wydajności z powodu leniwej oceny (a przynajmniej nie trafień wydajnościowych). Nie sprzeciwiam się ich używaniu, tylko wskazując, że jeśli masz zamiar, powinieneś wziąć pod uwagę ostrzeżenia osób wdrażających.
Adam Parkin
4
@SeanPatrickFloyd teraz, gdy Java 8 jest już dostępna, chcesz zaktualizować to na przykładzie obejmującym lambdy? JakCollections2.transform(input -> Integer.toHexString(intput.intValue())
Daniel Lubarov
2
@Daniel W Javie 8 nie widzę powodu, aby robić to w przypadku guawy. Zamiast tego wybrałbym odpowiedź Leventova
Sean Patrick Floyd
92

Od wersji Java 8 istnieje kilka standardowych opcji umożliwiających to w 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());

Zobacz java.util.Collection.stream()i java.util.stream.Collectors.toList().

Leventov
źródło
140
To jest tak dużo gadatliwe, że rani mnie w środku.
Natix
1
@Natix zgadza się co do toList(). Wymiana na inny typ:(List<R>)((List) list).replaceAll(o -> doMap((E) o));
lewentow
2
Czy e -> doMap(e)można zastąpić tylko doMap?
jameshfisher
3
@jameshfisher, tak, coś w rodzaju foo::doMaplub Foo::doMap.
leventov
9
Myślę, że właśnie dlatego istnieje Scala. Poczekaj, aż Java 12 będzie mieć coś czytelnego.
JulienD
26

Istnieje wspaniała biblioteka o nazwie Functional Java, która obsługuje wiele rzeczy, które chciałbyś mieć Java, ale tak nie jest. Z drugiej strony jest też ten wspaniały język Scala, który robi wszystko, co powinna była zrobić Java, ale nie robi tego, jednocześnie będąc kompatybilnym ze wszystkim, co napisano dla JVM.

pszenicy
źródło
Interesuje mnie, w jaki sposób umożliwili następującą składnię: a.map({int i => i + 42});czy rozszerzyli kompilator? lub dodany preprocesor?
Andrey,
@Andrey - Możesz sam ich o to zapytać lub sprawdzić kod źródłowy, aby zobaczyć, jak to się robi. Oto link do źródła: functionaljava.org/source
Wheaties
1
@Andrey: przykłady wykorzystują składnię z propozycji zamknięć BGGA. Chociaż istnieje uruchomiony prototyp, nie jest jeszcze w „oficjalnej” Javie.
Peter Štibraný
@Andrey: ta składnia jest częścią proponowanej specyfikacji zamknięć w Javie (zobacz przedostatni akapit na stronie głównej). Jest tylko prototypowa implementacja.
Michael Borgwardt
2
Scala thread hijack :( Mam nadzieję, że SO nie będzie podobna do listy mailingowej JavaPosse;)
Jorn
9

Uważaj Collections2.transform()na guawy. Największą zaletą tej metody jest też największe niebezpieczeństwo: jej lenistwo.

Spójrz na dokumentację Lists.transform(), która moim zdaniem dotyczy również Collections2.transform():

Funkcja jest stosowana leniwie, wywoływana w razie potrzeby. Jest to konieczne, aby zwrócona lista była widokiem, ale oznacza to, że funkcja będzie stosowana wiele razy do operacji zbiorczych, takich jak List.contains (java.lang.Object) i List.hashCode (). Aby to działało dobrze, funkcja powinna być szybka. Aby uniknąć leniwej oceny, gdy zwracana lista nie musi być widokiem, skopiuj zwróconą listę do nowej wybranej listy.

Również w dokumentacji Collections2.transform()wspominają, że masz podgląd na żywo, że zmiana na liście źródeł wpływa na przekształconą listę. Takie zachowanie może prowadzić do trudnych do wyśledzenia problemów, jeśli programista nie zdaje sobie sprawy, jak to działa.

Jeśli chcesz mieć bardziej klasyczną „mapę”, która będzie działać tylko raz, to lepiej będzie, jeśli chcesz FluentIterable, również z Guawy, która ma znacznie prostszą operację. Oto przykład google:

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

transform()tutaj jest metoda mapy. Używa tej samej funkcji <> "wywołania zwrotne" co Collections.transform(). Lista, którą otrzymujesz, jest jednak tylko copyInto()do odczytu , użyj jej, aby uzyskać listę do odczytu i zapisu.

W przeciwnym razie, oczywiście, gdy java8 wyjdzie z lambdami, będzie to przestarzałe.

Emmanuel Touzery
źródło
2

Choć to stare pytanie, chciałbym pokazać inne rozwiązanie:

Po prostu zdefiniuj własną operację za pomocą generycznych java i strumieni Java 8:

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

Niż możesz napisać taki kod:

List<String> hex = map(Arrays.asList(10, 20, 30, 40, 50), Integer::toHexString);
IPP Nerd
źródło