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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3254072/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:17:56  来源:igfitidea点击:

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

javamemoryscalaiteratorhashmap

提问by Skuge

I created a class Foothat has the method toArray()that returns an Array<Int>.

我创建了一个Foo具有toArray()返回Array<Int>.

Now, I have a HashMap mapping Strings to HashMaps, which map Objects to Foo. That is:

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

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. 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.

类似的代码工作得很好,但 HashMap 的大小太大,无法在内存中容纳其中的两个。如您所见,我添加了两个要清除某些条目的点。问题是,如果我这样做,我会收到并发错误,或者迭代器循环终止。

I wonder if there is a better way to iterate through the Maps and copy the information.

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

Also, I'm working in a Scala project but here I have to use Java types for some compatibility issues. Although Java.util.HashMapis not an iterator, maybe Scala has some hidden functinality to deal with this?

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

Thanks,

谢谢,

回答by Gunslinger47

Iterators offer remove(..)methods that safely removes the previously accessed item. 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.

迭代器提供remove(..)了安全删除先前访问过的项目的方法。迭代映射的键/值条目,转换它们并将它们添加到新映射中,并随时删除旧的。

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

回答by Sandor Murakozi

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 地图(假设您使用的是 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. 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.

它将使用您的外部地图,并用新地图“替换”所有内部地图,其中值是 Int 数组。键和地图的一般“结构”保持不变。根据scaladoc“生成的地图在不复制任何元素的情况下包装原始地图。”,因此它不会是真正的替代品。

If you also do an

如果你也做一个

import scala.collection.JavaConversions._

then the java maps can be used the same way as scala maps: JavaConversionscontain 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.

顺便说一句,使用 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}}
Map(a -> Map(b -> 2))

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

Showing that the implicits are applied both to the outer and inner map quite nicely. 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).
You don't have to do anything else, just import JavaConversions._

表明隐式很好地应用于外部和内部映射。这是 JavaConversions 对象的目的:即使您有一个 java 集合,您也可以将其用作类似的 Scala 类(具有增强功能)。
您不必做任何其他事情,只需导入 JavaConversions._

回答by Manu

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

回答by user392486

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. 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.

你为什么不叫remove ()上迭代器或方法set.remove (iterator.next ()),其中iterator.next ()返回键,设置为专用话机和迭代的迭代器。

PS: also try to refactor your data structure, maybe some intermediate classes which handle the data retrieval? A map in a map with arrays as values doesn't say anything and is difficult to keep track of.

PS:也尝试重构你的数据结构,也许是一些处理数据检索的中间类?以数组作为值的映射中的映射没有说明任何内容并且难以跟踪。