51

I have a list of strings and I have a function to generate a value for each key in the list.

I want to create a map using this function. Can I do this with Google collections?

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
oshai
  • 14,865
  • 26
  • 84
  • 140
  • I assume you know this, but if the value the function generates could be the same for any two elements in the original list, you'd need to use `Multimaps.index` to generate a `Multimap` that can store multiple values for the same key. – ColinD Feb 15 '11 at 15:13

5 Answers5

88

Use Maps.uniqueIndex(Iterable, Function) :

Returns an immutable map for which the Map.values() are the given elements in the given order, and each key is the product of invoking a supplied function on its corresponding value.(from javadoc)

Example:

Map<String,String> mappedRoles = Maps.uniqueIndex(yourList, new Function<String,String>() {
  public String apply(String from) {
    // do stuff here
    return result;
  }});
Jon
  • 9,156
  • 9
  • 56
  • 73
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • 2
    What is the performance of this solution? I would like to use it for 50,000+ elements in a list of large objects. I currently use two separate maps, that store the same objects, but mapped to different keys. I'd like to get rid of these maps and use a list (since the keys are already fields of the object). And your solution allow me to generate the maps automatically from the list. But at what cost? – m_pGladiator Aug 22 '11 at 12:50
  • @m_pGladiator I know your question is old, but the answer might interest other people... I would say the Guava methods are provided more for convenience / readability than for performance, in general. If you have a doubt, just stick to manually crafted loop. Or use a tool like Caliper to measure respective performances. – PhiLho Sep 25 '15 at 13:36
  • The OP says they have "a [function] to generate value for each key in the list". Maps.uniqueIndex generates key for each value in the list, which is the reverse of what the OP asked for. therefore this answer and Sean's asnwer are incorrect and ColinD's answer is. – Marcin May 10 '16 at 17:09
23

As of 7/26/2012, Guava master contains two new ways to do this. They should be in release 14.0.

Maps.asMap(Set<K>, Function<? super K, V>) (and two overloads for SortedSet and NavigableSet) allows you to view a Set plus a Function as a Map where the value for each key in the set is the result of applying the function to that key. The result is a view, so it doesn't copy the input set and the Map result will change as the set does and vice versa.

Maps.toMap(Iterable<K>, Function<? super K, V>) takes an Iterable and eagerly converts it to an ImmutableMap where the distinct elements of the iterable are the keys and the values are the results of applying the function to each key.

ColinD
  • 108,630
  • 30
  • 201
  • 202
15

EDIT: It's entirely possible that Sean's right and I misunderstood the question.

If the original list is meant to be keys, then it sounds like you might be able to just use a computing map, via MapMaker.makeComputingMap, and ignore the input list to start with. EDIT: As noted in comments, this is now deprecated and deleted in Guava 15.0. Have a look at CacheBuilder instead.

On the other hand, that also doesn't give you a map which will return null if you ask it for a value corresponding to a key which wasn't in the list to start with. It also won't give you In other words, this may well not be appropriate, but it's worth consideration, depending on what you're trying to do with it. :)

I'll leave this answer here unless you comment that neither approach here is useful to you, in which case I'll delete it.


Original answer

Using Guava you can do this pretty easily with Maps.uniqueIndex:

Map<String, String> map = Maps.uniqueIndex(list, keyProjection);

(I mentioned Guava specifically as opposed to Google collections, as I haven't checked whether the older Google collections repository includes Maps.uniqueIndex.)

Grzegorz Rożniecki
  • 27,415
  • 11
  • 90
  • 112
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
13

Either I have misunderstood you or the other posters have. I understand that you want your list to be the map keys, while Maps.uniqueIndex() creates keys to map to your values (which is quite the opposite).

Anyway, there is an open Guava issue that requests the exact functionality you are requesting, and I have also implemented such a solution for a previous question.

Community
  • 1
  • 1
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
  • 1
    Yup, I think you may be right. I've edited my answer to mention computing maps, in case that would suit the OP, but I'm very ready to delete the answer completely. – Jon Skeet Feb 18 '11 at 07:04
6

Using Guava + Lambda

 Map<String, YourCustomClass> map = Maps.uniqueIndex(YourList, YourCustomClass -> YourCustomClass.getKey());
riddle_me_this
  • 8,575
  • 10
  • 55
  • 80
Carlos
  • 159
  • 2
  • 3