【发布时间】:2011-11-01 02:20:57
【问题描述】:
我需要创建逆映射 - 选择唯一值并为它们找到键。
似乎唯一的方法是迭代所有键/值对,因为 entrySet 返回集合
【问题讨论】:
-
您能否举例说明您希望如何处理重复值,例如 {1->3, 2->3}?
标签: java dictionary
我需要创建逆映射 - 选择唯一值并为它们找到键。
似乎唯一的方法是迭代所有键/值对,因为 entrySet 返回集合
【问题讨论】:
标签: java dictionary
地图中的值可能不是唯一的。但如果他们是(在你的情况下)你可以按照你在问题中写的那样做,并创建一个通用方法来转换它:
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") 或类似的东西。
【讨论】:
toMap(Entry::getValue, Entry::getKey)),而不是使用一个方法引用和一个 lambda。
示例用法
Map<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
Map<String, Integer> inverted = HashBiMap.create(map).inverse();
【讨论】:
在 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);
【讨论】:
似乎唯一的方法是迭代所有键/值对,因为 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);
【讨论】:
我会给出另一种解决这个问题的方法,给出一个额外的维度: 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]
【讨论】:
Apache Commons Collections 还为双向地图提供了一个BidiMap 接口,以及几个实现。
【讨论】:
您必须假设值可能相同,因为 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”样式检查,并允许您显示所有原始键和值。
【讨论】:
用番石榴
Multimaps.transformValues(Multimaps.index(map.entrySet(), Map.Entry::getValue),
Map.Entry::getKey)
作为回报,您会得到一个多重映射(基本上是列表映射)。
【讨论】:
如果您的值重复并且您需要将键存储在列表中,您可以使用
val invertedMap = originalMap.entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toList()))
);
【讨论】: