将Map<String, Boolean>反转为Map<Boolean, List<String>>

4

有没有更加优雅或内置的方法来反转HashMap的键和值?

我目前有以下代码。

private Map<Boolean, List<String>> reverseMap(Map<String, Boolean> permissions) {
    List<String> allow = new ArrayList<String>();
    List<String> deny = new ArrayList<String>();
    Map<Boolean, List<String>> returnvalue = new HashMap<Boolean, List<String>>();

    for (Entry<String, Boolean> entry : permissions.entrySet()) {
        if(entry.getValue()) {
            allow.add(entry.getKey());
        } else {
            deny.add(entry.getKey());
        }
    }

    returnvalue.put(true, allow);
    returnvalue.put(false, deny);
    return returnvalue;
}

6
你真的需要一个布尔值映射吗?因为它只有两个可能的键(true和false)。你考虑使用两个String集合,一个denyList(拒绝名单)和一个allowList(允许名单)吗? - Thilo
@Thilo - 别忘了 null :) 但是,我同意。 - Paul Bellora
4个回答

6

您可以考虑使用GuavaMultimap实现之一。例如:

private Multimap<Boolean, String> reverseMap(Map<String, Boolean> permissions) {
   Multimap<Boolean, String> multimap = ArrayListMultimap.create();
   for (Map.Entry<String, Boolean> entry : permissions.entrySet()) {
      multimap.put(entry.getValue(), entry.getKey());
   }
   return multimap;
}

更一般地说:
private static <K, V> Multimap<V, K> reverseMap(Map<K, V> source) {
   Multimap<V, K> multimap = ArrayListMultimap.create();
   for (Map.Entry<K, V> entry : source.entrySet()) {
      multimap.put(entry.getValue(), entry.getKey());
   }
   return multimap;
}

1

我会做类似的事情(但如果你经常这样做,请考虑使用Guava),只是将List替换为Set(似乎更一致),并预先填充reversemap:

private Map<Boolean, Set<String>> reverseMap(Map<String, Boolean> permissions) {
    Map<Boolean, Set<String>> returnvalue = new HashMap<Boolean, Set<String>>();
    returnvalue.put(Boolean.TRUE, new HashSet<String>());
    returnvalue.put(Boolean.FALSE, new HashSet<String>());
    for (Entry<String, Boolean> entry : permissions.entrySet()) 
        returnvalue.get(entry.getValue()).add(entry.getKey());
    return returnvalue;
}

1
首先要注意的是,如果你的值只有 true 或 false,那么你并不需要一个反向映射表。只有在你的值具有更广泛的范围时才有意义。
一种简单(但不太优雅)的方法是获取具有特定值的条目:
public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
     Set<T> keys = new HashSet<T>();
     for (Entry<T, E> entry : map.entrySet()) {
         if (entry.getValue().equals(value)) {
             keys.add(entry.getKey());
         }
     }
     return keys;
}

如果您需要经常调用它,那么您会发现这并不是很好。最好有两个不同的映射(正向和反向),并将条目添加到两个映射中。由于键和值之间没有1:1的关系,因此无法使用Bidi映射。

更新:以下解决方案行不通。请参见评论。 您还可以考虑使用TreeMap,并根据值进行排序。这样,您可以随时通过调用map.entrySet()来获得排序后的集合(首先拒绝条目,然后允许)。缺点是只有一个集合。

ValueComparator bvc =  new ValueComparator(map);
TreeMap<String,Boolean> sorted_map = new TreeMap(bvc);

class ValueComparator implements Comparator {
  Map base;

  public ValueComparator(Map base) {
      this.base = base;
  }

  public int compare(Object a, Object b) {
    return (Boolean)base.get(a).compareTo((Boolean)base.get(b));
  }
}


我认为在另一个映射中按值排序的 TreeMap 不是一个好主意 - 如果基本映射的内容发生变化,则 TreeMap 中的树将不一致,导致意外结果。 - Timothy Jones
我想使用TreeMap而不是初始的HashMap,抱歉造成困惑;-) - n0rm1e
如果base是您要插入的TreeMap,则在插入新内容时会出现空指针异常(因为base.get(newKey)将为空)。我认为您无法安全地按值排序TreeMap :( - Timothy Jones
编写一个适当的compare()方法留给读者作为家庭作业练习;-) 顺便说一下,这个比较方法由TreeMap调用,不会在任何东西上调用。 - n0rm1e
比较器将被TreeMap用于键查找,这将防止Map正常工作。我认为您不能以这种方式使用TreeMap - 请参见https://dev59.com/_HE85IYBdhLWcg3wQxLG#2864923或https://dev59.com/d2s05IYBdhLWcg3wFN4h#7465402。 - Timothy Jones

0

GuavaBiMap已经提供了一个反转其键值对的方法。也许您可以将相关Map的接口更改为BiMap,或者使用以下代码:

private BiMap<Boolean, String> reverseMap(Map<String, Boolean> permissions) {
   BiMap<String, Boolean> bimap = HashBiMap.create(permissions);
   return bimap.inverse();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接