嵌套映射中的java迭代器(Map 2D)

时间:2012-05-06 17:29:26

标签: java map iterator nested

我想知道:我如何迭代2D地图?我有一个中心地图:

private final Map<String, Map<String, String>> centralMap = 
    new HashMap<String, Map<String, String>>();

包含另一张地图:

Map<String,String> nestedMap = new HashMap<String, String>();

作为Value,第二个是在“put”方法中创建的,因此在构造函数中,我只有centralMap。现在我想重新定义这个方法并获得完整的地图输入(2个键和每个元素的值)

public Iterator<Entry> iterator()

我该怎么办?如果可能的话,如何在没有问题的情况下通过迭代器删除元素?

3 个答案:

答案 0 :(得分:3)

迭代器用于对集合进行操作,例如第一个映射的键集,或嵌套值的值(映射集合)。您不能指望迭代器renove方法能够理解您的复杂结构。

我建议您使用自己的方便方法为此构建自己的类。

此外,在这里继续:确保你不只是想拥有一个多图。如果是这样,请查看,例如,guava's HashMultimap

答案 1 :(得分:2)

您应用与在单个地图上迭代相同的过程,只需执行两次:

public void printNestedMap(Map<String, Map<String, String>> map)
  Iterator it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry pairs = (Map.Entry)it.next(); // <- pairs.getValue() is a map
        System.out.println("Key1: " + pairs.getKey());
        //print the inner Map
        printMap((Map<String, String>)pairs.getValue());
        it.remove(); // avoids a ConcurrentModificationException
    }
}

修改

将迭代在单个地图上移动到另一个要在此场景中调用的方法实际上会更好。

public void printMap(Map<String, String>> map)
{
   Iterator it = map.entrySet().iterator();
   while(it.hasNext())
   {
      Map.Entry pairs = (Map.Entry)it.next(); // <- pairs.getValue() is a String
      System.out.println("Key2: " + pairs.getKey() + " Value2: " + pairs.getValue());
      it.remove();
   }
}

编辑2:测试计划

   import java.util.*;
   public class TestMap
   {
      public static void main(String[] args)
      {
         Map<String, String> innerMap              = new HashMap<>();
         Map<String, Map<String, String>> outerMap = new HashMap<>();

         innerMap.put("Key1", "Val1");
         innerMap.put("Key2", "Val2");
         innerMap.put("Key3", "Val3");
         innerMap.put("Key4", "Val4");

         outerMap.put("OuterKey1", innerMap);
         printNestedMap(outerMap);
      }

      public static void printNestedMap(Map<String, Map<String, String>> map)
      {
         Iterator it = map.entrySet().iterator();
         while (it.hasNext()) {
            Map.Entry pairs = (Map.Entry)it.next(); // <- pairs.getValue() is a map
            System.out.println("Key1: " + pairs.getKey());
            //print the inner Map
            printMap((Map<String, String>)pairs.getValue());
            it.remove(); // avoids a ConcurrentModificationException
         }
      }

      public static void printMap(Map<String, String> map)
      {
         Iterator it = map.entrySet().iterator();
         while(it.hasNext())
         {
            Map.Entry pairs = (Map.Entry)it.next(); // <- pairs.getValue() is a String
            System.out.println("Key2: " + pairs.getKey() + " Value2: " + pairs.getValue());
            it.remove();
         }
      }
   }

输出:

Key1: OuterKey1
Key2: Key2 Value2: Val2
Key2: Key1 Value2: Val1
Key2: Key4 Value2: Val4
Key2: Key3 Value2: Val3

答案 2 :(得分:2)

如果你想获得包含两个键和值的Map.Entry个元素,那么创建一个将{2}组合在一个元素中的两个键并使用它的类Pair<String, String>真的会更自然作为单个地图中的关键而不是嵌套地图。

如果你这样做,你的主要结构将是Map<Pair<String, String>, String>并且使用Map.entrySet()方法将为你提供一个Set<Map.Entry<String, String>, String>,你可以从中获得一个迭代器,它可以提供你的大概。重新开始。

如果由于其他原因需要Map<String, Map<String, String>>,也可以通过相当简单的代码将其转换为上述结构,这可能是从中获取信息的最明智的方法

编辑注意:

上述Pair类与Map.Entry基本相同,因此您可以通过构建Map<Map.Entry<String, String>, String>来避免为密钥创建新类。我认为它使代码不那么清晰,但它肯定可以在功能上等同。

示例代码

在下面的代码中,我将Pair类定义为内部静态(实际使用,您可能希望提取为独立类),并编写一个转换,在您描述时采用嵌套映射,将其转换为我建议的形式,并在转换后的地图的条目上使用迭代器来打印值。

迭代器当然可以用于其他事情,convert方法和Pair类是通用的。

import java.util.*;

public class TestMap
{
    public static void main(String[] args)
    {
        Map<String, String> innerMap1 = new HashMap<String, String>();
        Map<String, String> innerMap2 = new HashMap<String, String>();
        Map<String, Map<String, String>> outerMap = new HashMap<String, Map<String, String>>();

        innerMap1.put("InnerKey1", "Val1");
        innerMap1.put("InnerKey2", "Val2");
        innerMap1.put("InnerKey3", "Val3");
        innerMap1.put("InnerKey4", "Val4");

        innerMap2.put("InnerKey5", "Val5");
        innerMap2.put("InnerKey6", "Val6");
        innerMap2.put("InnerKey7", "Val7");
        innerMap2.put("InnerKey8", "Val8");

        outerMap.put("OuterKey1", innerMap1);
        outerMap.put("OuterKey2", innerMap2);

        Map<Pair<String, String>, String> convertedMap = convert(outerMap);
        for (Map.Entry<Pair<String, String>, String> entry: convertedMap.entrySet()) {
            System.out.println(String.format("OuterKey: %s, InnerKey: %s, Value: %s",
                    entry.getKey().getFirst(),
                    entry.getKey().getSecond(),
                    entry.getValue()
            ));
        }
    }

    private static <K1,K2,V> Map<Pair<K1, K2>,V> convert(Map<K1, Map<K2,V>> nestedMap) {
        Map<Pair<K1, K2>, V> result = new HashMap<Pair<K1, K2>, V>();
        for (Map.Entry<K1, Map<K2, V>> outerEntry: nestedMap.entrySet()) {
            final K1 outerKey = outerEntry.getKey();
            for (Map.Entry<K2, V> innerEntry: outerEntry.getValue().entrySet()) {
                final K2 innerKey = innerEntry.getKey();
                final V value = innerEntry.getValue();
                result.put(new Pair<K1, K2>(outerKey, innerKey), value);
            }
        }
        return result;
    }

    public static class Pair<T1, T2> {

        private T1 first;
        private T2 second;

        public Pair(T1 first, T2 second) {
            this.first = first;
            this.second = second;
        }

        public T1 getFirst() {
            return first;
        }

        public T2 getSecond() {
            return second;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Pair pair = (Pair) o;

            if (first != null ? !first.equals(pair.first) : pair.first != null) return false;
            if (second != null ? !second.equals(pair.second) : pair.second != null) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = first != null ? first.hashCode() : 0;
            result = 31 * result + (second != null ? second.hashCode() : 0);
            return result;
        }

    }
}

请注意上下文中的用法:

在您当前的代码中,您有一个字段centralMap的类,它是旧嵌套表单中的映射,以及一个用于映射大小的整数计数器。

此包含类有一个添加如下所示条目的方法:

@Override
public String put(final String row, final String column, final String value) {
    /**
     * Second map which is contained by centralMap, that contain Strings as Keys
     * and Values.
     */
    Map<String, String> nestedMap;

    if (centralMap.containsKey(row))
        nestedMap = centralMap.get(row);
    else
        nestedMap = new HashMap<String, String>();
    if (!nestedMap.containsKey(column))
        counter++;
    centralMap.put(row, nestedMap);
    return nestedMap.put(column, value);
}

如果不是使用嵌套地图,而是将此字段更改为建议表单的地图,此方法将变得更简单:

@Override
public String put(final String row, final String column, final String value) {
    Pair<String, String> key = new Pair(row, column);
    if (centralMap.contains(key)
        counter++;
    centralMap.put(key, value);
}

你实际上不再需要计数器了,因为它总是包含与centralMap.size()相同的值。

<强>更新

从昨天的编辑中删除但现在已删除,现在我(从编辑历史记录)清楚地想要构建一个以正确的顺序委托给地图的所有迭代器的迭代器,并返回一个包含两者的简单结构键和值。

这当然是可能的,如果我以后有时间,我可能会为它添加一些示例代码。正如在另一个回复中所指出的,iterator.remove()方法可能是不可能的或不自然的。

与此同时,您的要求(如同对其他回复的评论所述)与番石榴Table提供的要求非常相似。这是开源的,看着它可能会给你一些想法。你可以download the source for guava here

具体来说,在番石榴的StandardTable中,有一个内部类CellIterator,它看起来像:

  private class CellIterator implements Iterator<Cell<R, C, V>> {
    final Iterator<Entry<R, Map<C, V>>> rowIterator
        = backingMap.entrySet().iterator();
    Entry<R, Map<C, V>> rowEntry;
    Iterator<Entry<C, V>> columnIterator
        = Iterators.emptyModifiableIterator();

    @Override public boolean hasNext() {
      return rowIterator.hasNext() || columnIterator.hasNext();
    }

    @Override public Cell<R, C, V> next() {
      if (!columnIterator.hasNext()) {
        rowEntry = rowIterator.next();
        columnIterator = rowEntry.getValue().entrySet().iterator();
      }
      Entry<C, V> columnEntry = columnIterator.next();
      return Tables.immutableCell(
          rowEntry.getKey(), columnEntry.getKey(), columnEntry.getValue());
    }

    @Override public void remove() {
      columnIterator.remove();
      if (rowEntry.getValue().isEmpty()) {
        rowIterator.remove();
      }
    }
  }

您不能只复制此代码,因为它取决于番石榴中的其他内容,但它显示了您必须执行的操作的基本模式。