2010-07-15 115 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 
    } 
}