【问题标题】:Java invert mapJava 反转映射
【发布时间】:2011-11-01 02:20:57
【问题描述】:

我需要创建逆映射 - 选择唯一值并为它们找到键。 似乎唯一的方法是迭代所有键/​​值对,因为 entrySet 返回集合 所以值不是唯一的?

【问题讨论】:

  • 您能否举例说明您希望如何处理重复值,例如 {1->3, 2->3}?

标签: java dictionary


【解决方案1】:

地图中的值可能不是唯一的。但如果他们是(在你的情况下)你可以按照你在问题中写的那样做,并创建一个通用方法来转换它:

private static <V, K> Map<V, K> invert(Map<K, V> map) {

    Map<V, K> inv = new HashMap<V, K>();

    for (Entry<K, V> entry : map.entrySet())
        inv.put(entry.getValue(), entry.getKey());

    return inv;
}

Java 8:

public static <V, K> Map<V, K> invert(Map<K, V> map) {
    return map.entrySet()
              .stream()
              .collect(Collectors.toMap(Entry::getValue, Entry::getKey));
}

使用示例:

public static void main(String[] args) {

    Map<String, Integer> map = new HashMap<String, Integer>();

    map.put("Hello", 0);
    map.put("World!", 1);

    Map<Integer, String> inv = invert(map);

    System.out.println(inv); // outputs something like "{0=Hello, 1=World!}"
}

旁注:put(.., ..) 方法将返回键的“旧”值。如果它不为 null,您可能会抛出 new IllegalArgumentException("Map values must be unique") 或类似的东西。

【讨论】:

  • 在这种情况下(例如,没有重复值),您可能希望使用 map.size() 的初始大小来初始化新地图。特别是对于大地图,这可以大大加快速度。
  • 在 Java 8 示例中,您可以对键和值使用方法引用(即toMap(Entry::getValue, Entry::getKey)),而不是使用一个方法引用和一个 lambda。
【解决方案2】:

看看Google Guava BiMap

示例用法

Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");

Map<String, Integer> inverted = HashBiMap.create(map).inverse();

【讨论】:

    【解决方案3】:

    在 java 8 中获取给定地图的倒置形式:

    public static <K, V> Map<V, K> inverseMap(Map<K, V> sourceMap) {
        return sourceMap.entrySet().stream().collect(
            Collectors.toMap(Entry::getValue, Entry::getKey,
               (a, b) -> a) //if sourceMap has duplicate values, keep only first
            );
    }
    

    示例用法

    Map<Integer, String> map = new HashMap<Integer, String>();
    
    map.put(1, "one");
    map.put(2, "two");
    
    Map<String, Integer> inverted = inverseMap(map);
    

    【讨论】:

      【解决方案4】:

      似乎唯一的方法是迭代所有键/​​值对,因为 entrySet 返回的值不唯一?

      至少是一种方式。这是一个例子:

      Map<Integer, String> map = new HashMap<Integer, String>();
      
      map.put(1, "one");
      map.put(2, "two");
      
      Map<String, Integer> inverted = new HashMap<String, Integer>();
      
      for (Integer i : map.keySet())
          inverted.put(map.get(i), i);
      

      在非唯一值的情况下,此算法会将找到的最后一个值映射到它的键。 (由于大多数地图的迭代顺序是未定义的,这应该与任何解决方案一样好。)

      如果您确实想保留为每个键找到的第一个值,您可以将其更改为

      if (!inverted.containsKey(map.get(i)))
          inverted.put(map.get(i), i);
      

      【讨论】:

        【解决方案5】:

        我会给出另一种解决这个问题的方法,给出一个额外的维度: EntrySet 中的重复值

        public static void main(String[] args) {
        
            HashMap<Integer, String> s = new HashMap<Integer, String>();
            s.put(1, "Value1");
            s.put(2, "Value2");
            s.put(3, "Value2");
            s.put(4, "Value1");
        
            /*
             * swap goes here
             */
            HashMap<String,List<Integer>> newMap = new HashMap<String, List<Integer>>();
            for (Map.Entry<Integer, String> en : s.entrySet()) {
                System.out.println(en.getKey() + " " + en.getValue());
        
                if(newMap.containsKey(en.getValue())){
                    newMap.get(en.getValue()).add(en.getKey());
                } else {
                    List<Integer> tmpList = new ArrayList<Integer>();
                    tmpList.add(en.getKey());
                    newMap.put(en.getValue(), tmpList);
                }
            }
        
            for(Map.Entry<String, List<Integer>> entry: newMap.entrySet()){
                System.out.println(entry.getKey() + " " + entry.getValue());
            }
        }
        

        T 结果将是:

        1 值1
        2 值2
        3 值2
        4 值1
        值1 [1, 4]
        值2 [2, 3]

        【讨论】:

          【解决方案6】:

          Apache Commons Collections 还为双向地图提供了一个BidiMap 接口,以及几个实现。

          BidiMap JavaDoc

          【讨论】:

            【解决方案7】:

            您必须假设值可能相同,因为 Map 合约允许这样做。

            在我看来,最好的解决方案是使用包装器。它将包含原始值,并添加一个 id。它的 hashCode() 函数将依赖于 id,并且您为原始值提供了一个 Getter。 代码是这样的:

            public class MapKey
            {
                /**
                 * A new ID to differentiate equal values 
                 */
                private int _id;
                /**
                 * The original value now used as key
                 */
                private String _originalValue;
            
                public MapKey(String originalValue)
                {
                    _originalValue = originalValue;
                   //assuming some method for generating ids...
                    _id = getNextId();
                }
            
                public String getOriginalValue()
                {
                    return _originalValue;
                }
            
                @Override
                public int hashCode()
                {
                    final int prime = 31;
                    int result = 1;
                    result = prime * result + _id;
                    return result;
                }
            
                @Override
                public boolean equals(Object obj)
                {
                    if (this == obj)
                        return true;
                    if (obj == null)
                        return false;
                    if (getClass() != obj.getClass())
                        return false;
                    MapKey other = (MapKey) obj;
                    if (_id != other._id)
                        return false;
                    return true;
                }
            
                @Override
                public String toString()
                {
                    StringBuilder sb = new StringBuilder();
                    sb.append("MapKey value is ");
                    sb.append(_originalValue);
                    sb.append(" with ID number ");
                    sb.append(_id);
                    return sb.toString();
                }
            

            反转地图是这样的:

            public Map <MapKey, Integer> invertMap(Map <Integer, String> map)
            {
            
                 Map <MapKey, Integer> invertedMap = new HashMap <MapKey, Integer>();
            
               Iterator<Entry<Integer, String>> it = map.entrySet().iterator();
            
               while(it.hasNext())
               {
                   //getting the old values (to be reversed)
                   Entry<Integer, String> entry = it.next();
                   Integer oldKey = entry.getKey();
                   String oldValue = entry.getValue();
            
                   //creating the new MapKey
                   MapKey newMapKey = new MapKey(oldValue);
                   invertedMap.put(newMapKey, oldKey);
               }
            
               return invertedMap;
            }
            

            像这样打印值:

            for(MapKey key : invertedMap.keySet())
                   {
                       System.out.println(key.toString() + " has a new value of " +  invertedMap.get(key));
            
                   }
            

            这些代码都没有经过测试,但我相信这是最好的解决方案,因为它使用 OO 继承设计而不是“c”样式检查,并允许您显示所有原始键和值。

            【讨论】:

              【解决方案8】:

              用番石榴

              Multimaps.transformValues(Multimaps.index(map.entrySet(), Map.Entry::getValue),
                      Map.Entry::getKey)
              

              作为回报,您会得到一个多重映射(基本上是列表映射)。

              【讨论】:

                【解决方案9】:

                如果您的值重复并且您需要将键存储在列表中,您可以使用

                val invertedMap = originalMap.entrySet().stream()
                        .collect(Collectors.groupingBy(
                                Map.Entry::getValue, 
                                Collectors.mapping(Map.Entry::getKey, Collectors.toList()))
                        );
                

                【讨论】:

                  猜你喜欢
                  • 2010-10-03
                  • 1970-01-01
                  • 2013-05-30
                  • 1970-01-01
                  • 2017-03-16
                  • 1970-01-01
                  • 2021-09-18
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多