在Java(或Scala)中迭代HashMap的HashMap。

6

我创建了一个名为 Foo 的类,该类具有方法 toArray(),返回类型为 Array<Int>

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

HashMap<String,HashMap<Object,Foo>>

我想要创建一个新的类型对象:

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

这是通过对原始HashMap中的每个元素Foo调用toArray()函数来获取的。

为此,我通常会执行以下操作:

    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的大小太大了,无法在内存中容纳两个HashMap。您可以看到我添加了两个点,我想在这里清除一些条目。问题是,如果我这样做,要么会出现并发错误,要么迭代器循环会终止。
我想知道是否有更好的方法来遍历Map并复制信息。
此外,我正在一个Scala项目中工作,但是在这里我必须使用Java类型进行某些兼容性问题。尽管Java.util.HashMap不是迭代器,但Scala可能有一些隐藏的功能来处理这个问题吗?
谢谢。
4个回答

8

迭代器提供了remove(..)方法,可以安全地删除之前访问过的项。遍历Map的键/值条目,将它们转换并添加到新的Map中,并在执行时删除旧的条目。

/**
 * 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());
        }
    }
}

5
例如,考虑字符串键;我们称其为输入data: 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
  }
}

5

我没有时间去检查,但我猜这样的代码应该可以在Scala Maps上运行(假设你使用的是最终发布的Scala 2.8版本):

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

它将获取你的外部地图,并用一个新的内部地图“替换”所有内部地图,其中值是Int数组。键和地图的一般“结构”保持不变。根据scaladoc,“生成的地图包装原始地图而不复制任何元素”,因此它不会真正替换。
如果您还执行
import scala.collection.JavaConversions._

然后,Java的映射可以像Scala的映射一样使用:JavaConversions 包含许多隐式方法,可以在Scala和Java集合之间进行转换。
顺便说一句,如果最终使用Map >> 可能不是很方便,如果我是你,我会考虑引入一些类来隐藏这种结构的复杂性。 编辑,以反映您的评论
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}}
Map(a -> Map(b -> 2))

这表明隐式转换很好地应用于外部和内部映射。这就是JavaConversions对象的目的:即使您拥有一个Java集合,也可以将其用作类似的Scala类(具有增强功能)。
您无需执行其他操作,只需导入JavaConversions._


谢谢,但尽管我在项目中使用Scala,HashMaps是Java HashMaps,所以您无法在其上调用mapValues。是否有一种使用JavaConversions解决此问题的方法? - Skuge

4
该集合由映射支持,因此对映射所做的更改会反映在集合中,反之亦然。如果在迭代器遍历集合期间修改了映射(除了通过迭代器自己的remove操作),则迭代结果是不确定的。该集合支持元素删除,可以通过Iterator.remove,Set.remove,removeAll,retainAll和clear操作从映射中删除相应的映射。
为什么不在迭代器上调用remove()方法或者set.remove(iterator.next()),其中iterator.next()返回键,set是键集,iterator是其迭代器。
另外,请尝试重构数据结构,也许一些中间类可以处理数据检索?嵌套映射和作为值的数组并没有提供清晰的信息,并且难以跟踪。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接