176

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...)

Will Ness
71.5k10 gold badges105 silver badges192 bronze badges
asked Oct 11, 2010 at 15:00
7
  • 2
    Duplicated (rather better) at stackoverflow.com/questions/3907412/… Commented Oct 11, 2010 at 15:03
  • 9
    @Chris: How is it the same question? Commented Oct 11, 2010 at 15:12
  • 1
    It's not the same question. It's closely related though... Commented Oct 11, 2010 at 15:12
  • 1
    If the answer to this question is yes, it answers also the other linked question. If the answer is no (and it seems so), they are completely unrelated. Commented Oct 11, 2010 at 15:17
  • 1
    As of Java8, This is what @delnan might have been referring to leveluplunch.com/java/examples/… Commented Jun 13, 2017 at 20:49

6 Answers 6

112

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().

Yasir Arsanukayev
9,7062 gold badges42 silver badges62 bronze badges
answered Jan 8, 2014 at 15:05
Sign up to request clarification or add additional context in comments.

12 Comments

This is so much verbose that is hurts me inside.
leventov: replaceAll() cannot map to a different type, is usable only for lists, and modifies the original list. That's not good. I can accept that you need to create a Stream from every collection before you can perform some functional operations on it, but WHY OH WHY are there no toList(), toSet() etc. methods directly on it? Who the hell cares about some collectors? I just want to map/filter a collection.
Can e -> doMap(e) be replaced with just doMap?
@jameshfisher, yes, something like foo::doMap or Foo::doMap.
I guess this is why Scala exists. Wait for Java 12 to have something readable.
|
92

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);
answered Oct 11, 2010 at 15:05

10 Comments

It's worth noting that while with Guava you can do this, you might not want to: code.google.com/p/guava-libraries/wiki/FunctionalExplained (read the "Caveats" section).
@AdamParkin true, but I'm pretty sure that refers to more advanced functional concepts than this, otherwise they wouldn't have developed the transform() methods in the first place
Actually, no, there is often a definite performance hit with functional idioms, which is why they stress you should only use the facilities if you are certain it meets the two criteria outlined: net savings of LOC for the codebase as a whole, and proven performance gains due to lazy evaluation (or at least not performance hits). Not arguing against the use of them, just indicating that if you're going to, you should heed the warnings of the implementers.
@SeanPatrickFloyd now that Java 8 is out, want to update this with an example involving lambdas? Like Collections2.transform(input -> Integer.toHexString(intput.intValue())
@Daniel In Java 8, I don't see a reason to do that with Guava. Instead I'd go for leventov's answer
|
28

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.

answered Oct 11, 2010 at 15:04

13 Comments

I am interested in how did they enable following syntax: a.map({int i => i + 42}); did they extend compiler? or added preprocessor?
@Andrey - You can either ask them that yourself or check out the source code to see how it's done. Here's a link to the source: functionaljava.org/source
@Andrey: examples use syntax from BGGA closures proposal. While there is running prototype, it's not in 'official' Java yet.
@Andrey: that syntax is part of a proposed specification for closures in Java (see second-to-last paragraph on homepage). There's only a prototypical implementation.
Scala thread hijack :( I hope SO won't become like the JavaPosse mailing list ;)
|
10

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.

patstuart
1,9901 gold badge20 silver badges30 bronze badges
answered May 31, 2013 at 10:18

Comments

2

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"
answered Dec 4, 2012 at 11:32

1 Comment

Here's a guide on how to use TotallyLazy: intrepidis.blogspot.com/2013/07/…
2

Even though it's an old question I'd like to show another solution:

Just define your own operation using java generics and 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());
}

Than you can write code like this:

List<String> hex = map(Arrays.asList(10, 20, 30, 40, 50), Integer::toHexString);
answered Oct 17, 2018 at 19:06

2 Comments

What if you start with a classic array. Many older functions still return those.
Just use Arrays.asList(): This method acts as bridge between array-based and collection-based APIs, in combination with Collection.toArray()

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.