简体   繁体   English

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

[英]Iterating over a HashMap of HashMaps in Java (or Scala)

I created a class Foo that has the method toArray() that returns an Array<Int> . 我创建了一个类Foo ,它具有返回Array<Int>的方法toArray()

Now, I have a HashMap mapping Strings to HashMaps, which map Objects to Foo. 现在,我有一个HashMap映射字符串到HashMaps,它将Objects映射到Foo。 That is: 那是:

HashMap<String,HashMap<Object,Foo>>

And I want to create a new object of type: 我想创建一个类型的新对象:

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

That is obtained by calling the function toArray() for every element Foo in the original HashMAp. 这是通过为原始HashMAp中的每个元素Foo调用函数toArray()获得的。

To do so I normally would do something like: 为此,我通常会这样做:

    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;
    }

A similar code works just fine, but the Size of the HashMap is too big to hold two of them in memory. 类似的代码工作正常,但HashMap的大小太大,无法在内存中保存其中两个。 As you can see I added two points where I want to clear some entries. 如你所见,我添加了两点,我想清除一些条目。 The problem is, if I do, I get either a concurrency error, or the iterator loop just terminates. 问题是,如果我这样做,我会得到一个并发错误,或者迭代器循环只是终止。

I wonder if there is a better way to iterate through the Maps and copy the information. 我想知道是否有更好的方法来迭代地图并复制信息。

Also, I'm working in a Scala project but here I have to use Java types for some compatibility issues. 此外,我正在使用Scala项目,但在这里我必须使用Java类型来解决一些兼容性问题。 Although Java.util.HashMap is not an iterator, maybe Scala has some hidden functinality to deal with this? 虽然Java.util.HashMap不是迭代器,但Scala可能有一些隐藏的功能来处理这个问题?

Thanks, 谢谢,

Iterators offer remove(..) methods that safely removes the previously accessed item. 迭代器提供remove(..)方法,可以安全地删除以前访问过的项目。 Iterate over the Key/Value entries of the map, converting them and adding them to the new map, and removing the old ones as you go. 迭代地图的键/值条目,转换它们并将它们添加到新地图中,并在移动时删除旧地图。

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

I did not have time to check it, but I guess something like this should work on scala Maps (assuming you use scala 2.8 which is finally here): 我没有时间检查它,但我想这样的东西应该适用于scala Maps(假设你使用scala 2.8,这终于在这里):

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

It would take your outer map, and "replace" all inner maps with a new one, where the values are the Int arrays. 它会占用你的外部地图,并用一个新地图“替换”所有内部地图,其中值是Int数组。 Keys, and the general "structure" of the maps remain the same. 键和地图的一般“结构”保持不变。 According to scaladoc "The resulting map wraps the original map without copying any elements.", so it won't be a real replacement. 根据scaladoc “生成的地图包裹原始地图而不复制任何元素。”,因此它不会是真正的替代品。

If you also do an 如果你也做了

import scala.collection.JavaConversions._

then the java maps can be used the same way as scala maps: JavaConversions contain a bunch of implicit methods that can convert between scala and java collections. 然后java映射可以像scala映射一样使用: JavaConversions包含一堆可以在scala和java集合之间转换的隐式方法。

BTW using a Map < String,HashMap < Object,Array < Int>>> might not be really convenient at the end, if I were you I would consider introducing some classes that would hide the complexity of this construct. BTW使用Map <String,HashMap <Object,Array <Int >>>最后可能不太方便,如果我是你,我会考虑引入一些隐藏这个结构复杂性的类。

Edit reflecting to your comment 编辑反映您的评论

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)))
  }
}

prints: 打印:

{a={b=1}} {A = {B = 1}}
Map(a -> Map(b -> 2)) 地图(a - >地图(b - > 2))

Showing that the implicits are applied both to the outer and inner map quite nicely. 显示implicits非常适用于外部和内部地图。 This is the purpose of the JavaConversions object: even if you have a java collection you can use it as a similar scala class (with boosted features). 这是JavaConversions对象的目的:即使您有一个java集合,您也可以将它用作类似的scala类(具有增强功能)。
You don't have to do anything else, just import JavaConversions._ 您不必执行任何其他操作,只需导入JavaConversions._

For example considering String keys; 例如,考虑字符串键; lets call the input data : Map<String, Map<String, Object>> 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
  }
}

The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. 该集由地图支持,因此对地图的更改将反映在集中,反之亦然。 If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. 如果在对集合进行迭代时修改了映射(除了通过迭代器自己的remove操作),迭代的结果是未定义的。 The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations. 该集支持元素删除,它通过Iterator.remove,Set.remove,removeAll,retainAll和clear操作从地图中删除相应的映射。

Why don't you call the remove () method on the iterator or set.remove (iterator.next ()) where iterator.next () returns the key, set is the keyset and iterator its iterator. 为什么不在迭代器或set.remove (iterator.next ())上调用remove ()方法,其中iterator.next ()返回键,set是键集,迭代器是迭代器。

PS: also try to refactor your data structure, maybe some intermediate classes which handle the data retrieval? PS:还尝试重构你的数据结构,也许是一些处理数据检索的中间类? A map in a map with arrays as values doesn't say anything and is difficult to keep track of. 地图中的数组作为值的地图没有说什么,很难跟踪。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM