views:

278

answers:

4

Hello! I am trying to make a method that takes the first Map whose values are sets and a second empty Map whose values are Lists and fills the second Map with the same key/value Mappings as the first. The 2nd map will have every key in the 1st Map, but associated with it is a List of all the same elements that are in the Set it maps to. Use the ArrayList in the Second Map. Here is the method I got.

public static<K,E> void values(Map<K, Set<E>> ml, Map<K, List<E>> m2){
  for (Map.Entry<K, Set<E>> e; e < ml.size(); ? // I am not sure what to write here: a e.hasNext() or a e.next)
  // then i have to use a put method right?
  m2.put(e.getKey(), new ArrayList<E>(? )) // I don't know how to get the value, would it just be the same as e.getKey() or e.value
}

Can you tell me how you would do this? or if there is anything wrong? Thank you for your help

A: 

I find while loops are better for iterating than for loops, but maybe it's just me :)

public static<K,E> void values(Map<K, Set<E>> m1, Map<K, List<E>> m2) {
    Iterator<K> iter = m1.keySet().iterator();
    while (iter.hasNext()) {
        K key = iter.next();
        Set<E> value = m1.get(key);
        m2.put(key, new ArrayList<E>(value));
    }
}
Spyder
Thank you so much! So, the keySet would just put the values from m1 into the iterator right?
Roxy
Yes. Keyset gives you a set of all the m1 keys :)Alternatively you can use the entry set, which avoids calling get() for every key.
Spyder
+1  A: 

Not 100% exactly what you mean, but how about this:

public static<K,E> void values(Map<K, Set<E>> m1, Map<K, List<E>> m2)
{
    for(K key : m1.keySet())
    {
        Set<E> source = m1.get(key);

        List<E> dest = m2.get(key);
        if(dest == null)
        {
            dest = new ArrayList<E>();
            m2.put(key, dest);
        }

        dest.addAll(source);
    } 
}
Outlaw Programmer
Thank you for the answer, but what is receiver?
Roxy
Whoops, I renamed 'receiver' to 'dest' but looks like I missed that line.
Outlaw Programmer
ok! I see Thanx
Roxy
+1  A: 

2 options:

public static <K, E> void values(Map<K, Set<E>> m1, final Map<K, List<E>> m2) {
 if (m1 == null)
  throw new IllegalArgumentException("null map 1");
 if (m2 == null)
  throw new IllegalArgumentException("null map 2");

 for (Map.Entry<K, Set<E>> e : m1.entrySet()) {
  m2.put(e.getKey(), new ArrayList<E>(e.getValue()));
 }
}

public static <K, E> Map<K, List<E>> values(Map<K, Set<E>> m) {
 if (m == null)
  throw new IllegalArgumentException("null map");

 Map<K, List<E>> m2 = new HashMap<K, List<E>>(Math.max(
   (int) (m.size() / 0.75f) + 1, 16), 0.75f);

 for (Map.Entry<K, Set<E>> e : m.entrySet()) {
  m2.put(e.getKey(), new ArrayList<E>(e.getValue()));
 }

 return m2;
}
bruno conde
Looks good but I think explicitly setting the initial capacity is a little overkill and might be confusing.
Outlaw Programmer
A: 

I highly recommend using MultiMap for this, if you can. Google has a wonderful interface Multimap. There are two subinterfaces:

  • SetMultimap<K, V> which is equivalent to Map<K, Set<V>>
  • ListMultimap<K, V> which is equivalent to Map<K, List<V>>

The interfaces abstract away a lot of the array/list complexities, and the desired method would look like:

public static <K, E> void values(Multimap<K, E> m1, Multimap<K, E> m2) {
    m2.putAll(m1);
}

(Needless to say you won't need to have an extra method in the first place).

The interface allows simple insertion pattern

Multimap<String, String> map1 = HashMultimap.create();
map1.put("a", "first");
map1.put("a", "second");
map1.put("b", "third");

map1.get("a");  // --> returns ["first", "second"]
map1.get("b");  // --> returns ["third"]
map1.get("c");  // --> returns [], empty set =)
notnoop