2010-07-15 139 views
5

我创建了一个类Foo,该类的方法toArray()返回Array<Int>在Java(或Scala)中对HashMap的HashMap进行迭代

现在,我有一个HashMap将字符串映射到HashMaps,它将对象映射到Foo。那就是:

HashMap<String,HashMap<Object,Foo>> 

我想创建类型的新对象:即通过调用函数指定者()在原HashMap中的每一个元素美孚获得

HashMap<String,HashMap<Object,Array<Int>>> 

要做到这一点,我通常会做这样的事情:

public static HashMap<String,HashMap<Object,Array<Int>>> changeMap(Map mpOld) { 
     Object key2; 
     String key1; 
     Iterator it2; 
     HashMap<String,HashMap<Object,Array<Int>>> mpNew= 
      new HashMap<String,HashMap<Object,Array<Int>>>() 
     Iterator it1 = mpOld.keySet().iterator(); 
     while (it1.hasNext()) { 
      key1=it1.next(); 
      it2= mpOld.get(key1).keySet().iterator(); 
      mpNew.put(key1,new HashMap<Object,Array<Int>>()) 
      while (it2.hasNext()) { 
       key2=it2.next(); 
       mpNew.get(key1).put(key2,mpOld.get(key1).get(key2).toArray()); 
       //TODO clear entry mpOld.get(key1).get(key2) 
      } 
      //TODO clear entry mpOld.get(key1) 
     } 
     return mpNew; 
    } 

类似的代码工作得很好,但HashMap中的尺寸太大容纳他们两个在内存中。正如你所看到的,我添加了两点,我想清除一些条目。问题是,如果我这样做,我会得到一个并发错误,或者迭代器循环刚刚结束。

我想知道是否有更好的方式来遍历地图并复制信息。

此外,我在一个Scala项目中工作,但在这里我必须使用Java类型来解决一些兼容性问题。尽管Java.util.HashMap不是迭代器,但Scala可能有一些隐藏的功能来处理这个问题?

感谢,

回答

7

迭代器报价remove(..)安全地删除以前访问的项目的方法。迭代地图上的Key/Value条目,将其转换并添加到新地图中,并随时移除旧的地图。

/** 
* Transfers and converts all entries from <code>map1</code> to 
* <code>map2</code>. Specifically, the {@link Foo} objects of the 
* inner maps will be converted to integer arrays via {@link Foo#toArray}. 
* 
* @param map1 Map to be emptied. 
* @param map2 Receptacle for the converted entries. 
*/ 
private static void transfer(Map<String, Map<Object, Foo>> map1 
     , Map<String, Map<Object, int[]>> map2) { 

    final Iterator<Entry<String, Map<Object, Foo>>> mapIt 
     = map1.entrySet().iterator(); 
    while (mapIt.hasNext()) { 
     final Entry<String, Map<Object, Foo>> mapEntry = mapIt.next(); 
     mapIt.remove(); 
     final Map<Object, int[]> submap = new HashMap<Object,int[]>(); 
     map2.put(mapEntry.getKey(), submap); 
     final Iterator<Entry<Object,Foo>> fooIt 
      = mapEntry.getValue().entrySet().iterator(); 
     while (fooIt.hasNext()) { 
      final Entry<Object,Foo> fooEntry = fooIt.next(); 
      fooIt.remove(); 
      submap.put(fooEntry.getKey(), fooEntry.getValue().toArray()); 
     } 
    } 
} 
4

我没有时间去检查它,但我想这样的事情应该在斯卡拉地图工作(假设你使用的Scala 2.8是终于在这里):

mpO.mapValues(_.mapValues(_.toArray)) 

它会取出你的外部地图,并用一个新的“替换”所有的内部地图,其中的值是Int数组。键和地图的一般“结构”保持不变。根据scaladoc“结果地图包装原始地图而不复制任何元素”,所以它不会成为真正的替代品。

如果你也做一个

import scala.collection.JavaConversions._ 

那么Java地图可以使用相同的方式,斯卡拉地图:JavaConversions包含了一堆,可Scala和Java集合之间的转换隐式方法。

使用地图的BTW < String,HashMap < Object,Array < Int >>>可能不太方便,如果我是你,我会考虑引入一些隐藏复杂构造的类。

编辑反映到您的评论

import scala.collection.JavaConversions._ 
import java.util.Collections._ 

object MapValues { 
    def main(args: Array[String]) { 
    val jMap = singletonMap("a",singletonMap("b", 1)) 
    println(jMap) 
    println(jMap.mapValues(_.mapValues(_+1))) 
    } 
} 

打印:

{A = {B = 1}}
地图(一 - >地图(二 - > 2))

表明这些implicits既适用于外部地图也适用于内部地图。这就是JavaConversions对象的目的:即使你有一个java集合,你也可以将它用作一个类似的scala类(具有增强的特性)。
你没有做别的事情,只需要导入JavaConversions._

+0

谢谢,不过虽然我使用Scala开发项目中,包含HashMap是Java HashMaps这样,所以你不能要求他们mapVAlues。 使用JavaConversions解决这个问题吗? – Skuge 2010-07-15 11:11:37

+0

你能解决你的问题吗?我的编辑有帮助吗? – 2010-07-19 07:01:51

3

set受映射支持,所以映射的变化也反映在集,反之亦然。如果在对集合进行迭代的过程中修改了映射(除了通过迭代器自己的删除操作),迭代的结果是未定义的。该集支持元素删除,通过Iterator.remove,Set.remove,removeAll,retainAll和clear操作从映射中删除相应的映射。

你为什么不呼吁迭代器或set.remove (iterator.next())其中iterator.next()返回键remove()方法,设置是密钥集和迭代的迭代器。

PS:也尝试重构你的数据结构,也许一些处理数据检索的中间类?地图中包含数组的值不会显示任何内容,也很难追踪。

3

例如考虑String键;让我们称之为输入数据Map<String, Map<String, Object>> data

for (Entry<String, Map<String, Tuple>> entry : data.entrySet()) { 
    String itemKey = entry.getKey(); 
    for (Entry<String, Object> innerEntry : entry.getValue().entrySet()) { 
    String innerKey = innerEntry.getKey(); 
    Object o = innerEntry.getValue(); 
    // whatever, here you have itemKey, innerKey and o 
    } 
}