Java: is there a map function?

I need a map function. Is there something like this in Java already?

(For those who wonder: I of course know how to implement this trivial function myself...)


Solution 1:

Since Java 8, there are some standard options to do this in 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());

See java.util.Collection.stream() and java.util.stream.Collectors.toList().

Solution 2:

There is no notion of a function in the JDK as of java 6.

Guava has a Function interface though and the
Collections2.transform(Collection<E>, Function<E,E2>)
method provides the functionality you require.

Example:

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

Output:

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

These days, with Java 8, there is actually a map function, so I'd probably write the code in a more concise way:

Collection<String> hex = input.stream()
                              .map(Integer::toHexString)
                              .collect(Collectors::toList);

Solution 3:

There is a wonderful library called Functional Java which handles many of the things you'd want Java to have but it doesn't. Then again, there's also this wonderful language Scala which does everything Java should have done but doesn't while still being compatible with anything written for the JVM.

Solution 4:

Be very careful with Collections2.transform() from guava. That method's greatest advantage is also its greatest danger: its laziness.

Look at the documentation of Lists.transform(), which I believe applies also to Collections2.transform():

The function is applied lazily, invoked when needed. This is necessary for the returned list to be a view, but it means that the function will be applied many times for bulk operations like List.contains(java.lang.Object) and List.hashCode(). For this to perform well, function should be fast. To avoid lazy evaluation when the returned list doesn't need to be a view, copy the returned list into a new list of your choosing.

Also in the documentation of Collections2.transform() they mention you get a live view, that change in the source list affect the transformed list. This sort of behaviour can lead to difficult-to-track problems if the developer doesn't realize the way it works.

If you want a more classical "map", that will run once and once only, then you're better off with FluentIterable, also from Guava, which has an operation which is much more simple. Here is the google example for it:

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

transform() here is the map method. It uses the same Function<> "callbacks" as Collections.transform(). The list you get back is read-only though, use copyInto() to get a read-write list.

Otherwise of course when java8 comes out with lambdas, this will be obsolete.

Solution 5:

This is another functional lib with which you may use map: http://code.google.com/p/totallylazy/

sequence(1, 2).map(toString); // lazily returns "1", "2"