52

I didn't find such a multimap construction... When I want to do this, I iterate over the map, and populate the multimap. Is there an other way?

final Map<String, Collection<String>> map = ImmutableMap.<String, Collection<String>>of(
            "1", Arrays.asList("a", "b", "c", "c"));
System.out.println(Multimaps.forMap(map));

final Multimap<String, String> expected = ArrayListMultimap.create();
for (Map.Entry<String, Collection<String>> entry : map.entrySet()) {
    expected.putAll(entry.getKey(), entry.getValue());
}
System.out.println(expected);

The first result is {1=[[a, b, c, c]]} but I expect {1=[a, b, c, c]}

Alexander Farber
  • 21,519
  • 75
  • 241
  • 416
sly7_7
  • 11,961
  • 3
  • 40
  • 54

8 Answers8

49

Assuming you have

Map<String, Collection<String>> map = ...;
Multimap<String, String> multimap = ArrayListMultimap.create();

Then I believe this is the best you can do

for (String key : map.keySet()) {
  multimap.putAll(key, map.get(key));
}

or the more optimal, but harder to read

for (Entry<String, Collection<String>> entry : map.entrySet()) {
  multimap.putAll(entry.getKey(), entry.getValue());
}
Zombies
  • 25,039
  • 43
  • 140
  • 225
Kevin Bourrillion
  • 40,336
  • 12
  • 74
  • 87
  • 2
    That's exactly what I did ! Be just warned, as Kevin pointed it here http://groups.google.com/group/guava-discuss/browse_thread/thread/255cabbcc70925b3 a Multimap is a Map, and then if in the original Map, you have an empty iterable as value, then, the entry "" will not be added the the multimap – sly7_7 Jun 23 '10 at 15:27
  • The miultimap create was util for me: Multimap multimap = ArrayListMultimap.create(); It is applicable to all Multimap know implemented classes. – Lorenzo Lerate Aug 02 '16 at 09:35
10

This question is a little old, but I thought I'd give an updated answer. With Java 8 you could do something along the lines of

ListMultimap<String, String> multimap = ArrayListMultimap.create();
Map<String, Collection<String>> map = ImmutableMap.of(
                           "1", Arrays.asList("a", "b", "c", "c"));
map.forEach(multimap::putAll);
System.out.println(multimap);

This should give you {1=[a, b, c, c]}, as desired.

Dillon Ryan Redding
  • 1,213
  • 12
  • 17
  • 1
    I left java world for quite a while now, but it's always good to stay in touch with the new syntax. It removes some noise, I like it. Thank your for the update. Maybe @kevin-bourrillion could update his answer with this syntax – sly7_7 Mar 02 '16 at 14:05
8

Here is a useful generic version that I wrote for my StuffGuavaIsMissing class.

/**
 * Creates a Guava multimap using the input map.
 */
public static <K, V> Multimap<K, V> createMultiMap(Map<K, ? extends Iterable<V>> input) {
  Multimap<K, V> multimap = ArrayListMultimap.create();
  for (Map.Entry<K, ? extends Iterable<V>> entry : input.entrySet()) {
    multimap.putAll(entry.getKey(), entry.getValue());
  }
  return multimap;
}

And an immutable version:

/**
 * Creates an Immutable Guava multimap using the input map.
 */
public static <K, V> ImmutableMultimap<K, V> createImmutableMultiMap(Map<K, ? extends Iterable<V>> input) {
  ImmutableMultimap.Builder<K, V> builder = ImmutableMultimap.builder();
  for (Map.Entry<K, ? extends Iterable<V>> entry : input.entrySet()) {
    builder.putAll(entry.getKey(), entry.getValue());
  }
  return builder.build();
}
Daniel Alexiuc
  • 13,078
  • 9
  • 58
  • 73
  • 1
    These are somewhat inflexible in that they force you into using a particular Multimap implementation. – Michael May 05 '17 at 16:15
  • 2
    @Michael Hmm I couldn't imagine many things *less* inflexible than a free open-source code snippet posted on the internet :) – Daniel Alexiuc May 15 '17 at 06:04
  • @DanielAlexiuc Hehe. That's not the kind of inflexibility I meant though. If you used the first function throughout your code then subsequently needed to change the type of `Multimap` returned in half of those cases, you're in for a bit of a maintenance nightmare. It's a better design to pass a `Multimap` in via a parameter, I reckon. – Michael May 15 '17 at 12:33
2

UPDATE: For what you're asking, I think you're going to need to fall back to Multimap.putAll.

Hank Gay
  • 70,339
  • 36
  • 160
  • 222
  • Thanks, but when I asked the question, I've already tried some functions of this class...and other ways... but the ONLY things which works for me is to iterate over the map... – sly7_7 Jun 22 '10 at 14:22
  • @Sylvian: since you marked this answer accepted, does it mean that your problem is now actually solved? It look like not since you didn't mention that in any way. Just wondering, it would otherwise have been a waste of time for others to post a more detailed/suitable answer. – BalusC Jun 22 '10 at 14:37
  • What about `Multimaps.newMultimap(map, new Supplier>(){ public List get() { return new ArrayList();}});` – Mark Peters Jun 22 '10 at 14:39
  • 1
    It does'nt work since Multimaps.newMultimap() required an empty map as first argument – sly7_7 Jun 22 '10 at 14:40
  • @Sylvain, ah OK I misunderstood the behaviour of that method. – Mark Peters Jun 22 '10 at 14:42
  • Mark - this doesn't do what you think. It only creates a new multimap based on the underlying maop instance. It expects map to be empty, and throws an exception otherwise – user44242 Jun 22 '10 at 14:46
  • 2
    @Mark Peters Yeah, the Javadoc on that suite of methods could use some help. That requirement should be on the param docs, not just in the exception docs. – Hank Gay Jun 22 '10 at 14:49
  • @Hank: that's what I also think, but before asking for an improvement or something like that, I would ask the question here :) – sly7_7 Jun 22 '10 at 14:49
2

You can use the com.google.common.collect.Multimaps.flatteningToMultimap as your stream collector.

Given map:

Map<Integer, Collection<String>> map = Map.of(
   1, List.of("a", "b", "c"),
   2, List.of("d", "e", "f"));

You can do the following:

Multimap<Integer, String> multimap = map.entrySet().stream()
    .collect(flatteningToMultimap(Map.Entry::getKey, it -> it.getValue().stream(), HashMultimap::create));
1

Following code without Google's Guava library. It is used for double value as key and sorted order

Map<Double,List<Object>> multiMap = new TreeMap<Double,List<Object>>();

for( int i= 0;i<15;i++)
{
    List<Object> myClassList = multiMap.get((double)i);
    if(myClassList == null)
    {
        myClassList = new ArrayList<Object>();
        multiMap.put((double) i,myClassList);
    }
    myClassList.add("Value "+ i);
}

List<Object> myClassList = multiMap.get((double)0);
if(myClassList == null)
{
    myClassList = new ArrayList<Object>();
    multiMap.put( (double) 0,myClassList);
}
myClassList.add("Value Duplicate");
for (Map.Entry entry : multiMap.entrySet()) 
{
  System.out.println("Key = " + entry.getKey() + ", Value = " +entry.getValue());
}
Somnath Kadam
  • 6,051
  • 6
  • 21
  • 37
1
LinkedListMultimap<String, String> mm = map.entrySet()
    .stream()
    .collect(
        () -> LinkedListMultimap.create(map.size()),
        (m, e) -> m.putAll(e.getKey(), e.getValue()),
        (m1, m2) -> m1.putAll(m2));
0

There is another method one can use: Multimaps#newMultimap

If you are using a collection extending List or Set, the docs recommend using the special methods Multimaps#newListMultimap and Multimaps#newSetMultimap.

import com.google.common.collect.Multimaps;

Multimaps.newListMultimap(myMap, ArrayList::new);
Multimaps.newSetMultimap(myMap, HashSet::new);
Sridhar Sarnobat
  • 25,183
  • 12
  • 93
  • 106
nathanfranke
  • 775
  • 1
  • 9
  • 19
  • Please note that `myMap` needs to be empty, otherwise it will throw an `IllegalArgumentException`. The Javadoc for the method states: > the multimap assumes complete ownership over of map and the lists returned by factory. Those objects should not be manually updated, **they should be empty when provided**... – higuaro Jul 20 '21 at 14:39