是否存在可以在现有密钥上添加别名的地图实现
SuperMap map = .....;
map.put("key1" , object1);
map.addAlias("key1" , "aliaskey"); // <== create alias
Object o = map.get("aliaskey"); // equals object1
由于
答案 0 :(得分:3)
不确定你想要使用它,或者它是否值得麻烦,但是到目前为止我在这个线程中看到的实现都存在别名与其别名的密钥断开连接的问题。例如,如果你说:
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);
}
}
}
答案 1 :(得分:2)
您可以将别名设为另一个指向相同值的键条目。
map.put("key1", object1);
map.put("aliaskey", map.get("key1")); // <== create alias
Object o = map.get("aliaskey"); // equals object1
答案 2 :(得分:2)
@DavidMoles是迄今为止唯一一位给你答案的人。所有其他人只是制作副本,这意味着如果您使用“别名”键更新条目,则“原始”键不会更新。
然而,这对我来说似乎都是一个糟糕的设计。地图中的多个键指向同一个键不是地图的预期语义。相反,我建议您在地图上执行外部的别名映射,并创建一个新类型以强制执行查找之前始终正确的unalias:
// 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);
}
}
请注意地图的类型Map<Aliaser<String>.Key, Integer>
而不是Map<String, Integer>
。这有助于我们通过使Java编译器断言(通过类型检查)我们在进行地图查找之前始终正确地取消所有String
密钥。
如果您使用Scala,则可以使用隐式转换自动执行所有String
- &gt; Aliaser<String>.Key
次转换,但在Java中,您仍然无法手动执行转换。您可以随时声明一些本地方法,使其更简洁 - 或者只使用比我在我的示例中使用的更短的变量/方法名称。
答案 3 :(得分:1)
我不确定这样的实现是否已存在,但您可以随意开发自己的实现:
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;
}
}
答案 4 :(得分:0)
只需使用不同的键将值放入地图:
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