是否存在可以在现有键上添加别名的映射实现
SuperMap map = .....;
map.put("key1" , object1);
map.addAlias("key1" , "aliaskey"); // <== create alias
Object o = map.get("aliaskey"); // equals object1
谢谢
不知道你想用它做什么,或者它是否值得麻烦,但到目前为止我在这个线程中看到的实现都存在别名与它的别名键断开连接的问题。例如,如果你说:
SuperMap<String, String> sm = new SuperMap<String, String>();
sm.put("Elvis", "Presley");
sm.addAlias("Priscilla", "Elvis");
System.out.println(sm.get("Elvis"));
System.out.println(sm.get("Priscilla"));
sm.put("Elvis", "Costello");
System.out.println(sm.get("Elvis"));
System.out.println(sm.get("Priscilla"));
你会得到:
Presley
Presley
Costello
Presley
(注意:这是SuperMap的原始实现,不是编辑版本。)
如果你想要一个真正的别名——如果你想得到Presley, Presley, Costello, Costello
——你将需要一些更复杂的东西。
下面的代码未经测试(整个企业似乎有点疯狂),但它应该可以工作:
public class AliasMap<K, V> extends AbstractMap<K, V>
{
private final Map<K, V> backingMap;
private final Map<K, K> aliasToRealKey;
public AliasMap ()
{
this( new HashMap<K, V>() );
}
public AliasMap ( Map<K, V> backingMap )
{
this.backingMap = backingMap;
aliasToRealKey = new HashMap<K, K>();
}
@Override
public Set<Entry<K, V>> entrySet ()
{
return new AliasAwareEntrySet<K, V>( aliasToRealKey, backingMap );
}
@Override
public V put ( K k, V v )
{
if ( aliasToRealKey.containsKey( k ) )
{
throw new IllegalArgumentException( "An alias '" + k + "' already exists in the map" );
}
return backingMap.put( k, v );
}
@Override
public V get ( Object o )
{
V v = backingMap.get( o );
if ( v == null )
{
K realKey = aliasToRealKey.get( o );
if ( realKey == null )
{
return null;
}
return backingMap.get( realKey );
}
return v;
}
public void alias ( K realKey, K alias )
{
if ( backingMap.containsKey( alias ) )
{
throw new IllegalArgumentException( "The key '" + alias + "' already exists in the map" );
}
aliasToRealKey.put( alias, realKey );
}
private static class AliasAwareEntrySet<K, V> extends AbstractSet<Entry<K, V>>
{
private Map<K, K> aliasToRealKey;
private Map<K, V> backingMap;
public AliasAwareEntrySet ( Map<K, K> aliasToRealKey, final Map<K, V> backingMap )
{
this.aliasToRealKey = aliasToRealKey;
this.backingMap = backingMap;
}
@Override
public Iterator<Entry<K, V>> iterator ()
{
return new AliasAwareEntryIterator<K, V>( backingMap, aliasToRealKey );
}
@Override
public int size ()
{
return backingMap.size() + aliasToRealKey.size();
}
}
private static class AliasAwareEntryIterator<K, V> implements Iterator<Entry<K, V>>
{
Set<Entry<K, V>> realEntries;
Set<K> aliasKeys;
Iterator<Entry<K, V>> realIterator;
Iterator<K> aliasIterator;
boolean isRealEntry = true;
private Map<K, V> backingMap;
private Map<K, K> aliasToRealKey;
public AliasAwareEntryIterator ( final Map<K, V> backingMap, Map<K, K> aliasToRealKey )
{
this.realEntries = backingMap.entrySet();
this.aliasKeys = aliasToRealKey.keySet();
realIterator = realEntries.iterator();
aliasIterator = aliasKeys.iterator();
this.backingMap = backingMap;
this.aliasToRealKey = aliasToRealKey;
}
public boolean hasNext ()
{
return realIterator.hasNext() || aliasIterator.hasNext();
}
public Entry<K, V> next ()
{
if ( realIterator.hasNext() )
{
return realIterator.next();
}
isRealEntry = false;
final K alias = aliasIterator.next();
final K realKey = aliasToRealKey.get( alias );
return new AliasAwareEntry( alias, realKey );
}
public void remove ()
{
if ( isRealEntry )
{
realIterator.remove();
}
else
{
aliasIterator.remove();
}
}
private class AliasAwareEntry implements Entry<K, V>
{
private final K alias;
private final K realKey;
public AliasAwareEntry ( K alias, K realKey )
{
this.alias = alias;
this.realKey = realKey;
}
public K getKey ()
{
return alias;
}
public V getValue ()
{
return backingMap.get( realKey );
}
public V setValue ( V v )
{
return backingMap.put( realKey, v );
}
}
}
public static void main ( String[] args )
{
AliasMap<String, String> sm = new AliasMap<String, String>();
sm.put( "Elvis", "Presley" );
sm.alias( "Elvis", "Priscilla" );
System.out.println( sm.get( "Elvis" ) );
System.out.println( sm.get( "Priscilla" ) );
sm.put( "Elvis", "Costello" );
System.out.println( sm.get( "Elvis" ) );
System.out.println( sm.get( "Priscilla" ) );
for ( String s : sm.keySet() )
{
System.out.println(s);
}
for ( Iterator<Entry<String, String>> iterator = sm.entrySet().iterator(); iterator.hasNext(); )
{
Entry<String, String> entry = iterator.next();
System.out.println( entry.getKey() + " : " + entry.getValue() );
if ( "Priscilla".equals( entry.getKey() ) )
{
iterator.remove();
}
}
for ( String s : sm.keySet() )
{
System.out.println(s);
}
}
}
您可以使别名只是指向相同值的另一个键条目。
map.put("key1", object1);
map.put("aliaskey", map.get("key1")); // <== create alias
Object o = map.get("aliaskey"); // equals object1
到目前为止,@DavidMoles 是唯一一个为您提供真正混叠的答案的人。所有其他人都只是制作副本,这意味着如果您使用“别名”键更新条目,则不会更新“原始”键。
然而,这一切对我来说似乎都是一个糟糕的设计。映射中的多个键指向同一个键不是映射的预期语义。相反,我建议您在地图之外进行别名映射,并创建一个新类型以强制您在进行查找之前始终正确地取消别名:
// AliasTest.java
import java.util.*;
public class AliasTest {
public static void main(String[] args) {
Map<Aliaser<String>.Key, Integer> map = new HashMap<Aliaser<String>.Key, Integer>();
Aliaser<String> aliaser = new Aliaser<String>();
map.put(aliaser.addKey("A"), 1);
map.put(aliaser.addKey("B"), 2);
map.put(aliaser.addKey("C"), 3);
aliaser.addAlias("A", "X");
map.put(aliaser.lookup("X"), 5);
System.out.println(map.get(aliaser.lookup("A")));
}
}
// Aliaser.java
import java.util.*;
public class Aliaser<T> {
public class Key { }
private Map<T, Key> aliases = new HashMap<T, Key>();
public Key addKey(T alias) {
Key key = new Key();
aliases.put(alias, key);
return key;
}
public void addAlias(T orig, T alias) {
aliases.put(alias, aliases.get(orig));
}
public Key lookup(T alias) {
return aliases.get(alias);
}
}
请注意地图如何具有 typeMap<Aliaser<String>.Key, Integer>
而不是Map<String, Integer>
. 这有助于我们让 Java 编译器断言(通过类型检查)我们总是String
在进行映射查找之前正确地取消所有键的别名。
如果您使用的是 Scala,则可以使用隐式转换自动执行所有String
->Aliaser<String>.Key
转换,但在 Java 中,您只能手动进行转换。不过,您始终可以声明一些本地方法以使其不那么冗长,或者只使用比我在示例中使用的更短的变量/方法名称。
我不确定这样的实现是否已经存在,但请随意开发自己的:
public class SuperMap<K, V> extends HashMap<K, V> {
private final HashMap<K, K> aliases = new HashMap<K, K>();
public void addAlias(final K alias, final K key) {
aliases.put(alias, key);
}
@Override
public V get(final Object key) {
if (keySet().contains(key)) {
return super.get(key);
} else if (aliases.keySet().contains(key)) {
return super.get(aliases.get(key));
}
return null;
}
}
只需使用不同的键将值放入映射中:
Map<String, Integer> map = new HashMap<>();
Integer value = 42;
map.put("firstKey", value);
map.put("secondKey", value);
System.out.println(map.get("firstKey") == map.get("secondKey"));
> true