1

既存のキーにエイリアスを追加できるマップ実装は存在しますか

SuperMap map = .....;
map.put("key1" , object1);
map.addAlias("key1" , "aliaskey"); // <== create alias
Object o = map.get("aliaskey"); // equals object1

ありがとう

4

5 に答える 5

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);
        }
    }
}
于 2013-03-18T21:38:45.023 に答える
2

@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);
  }

}

マップがMap<Aliaser<String>.Key, Integer>ではなくtype を持っていることに注意してくださいMap<String, Integer>Stringこれは、マップ ルックアップを実行する前にすべてのキーのエイリアスを常に適切に解除することを Java コンパイラに (型チェックを通じて) アサートさせることで役立ちます。

Scala を使用していた場合は、すべてのString->Aliaser<String>.Key変換を暗黙的な変換で自動的に行うことができますが、Java では変換を手動で行う必要があります。ただし、ローカル メソッドを宣言して冗長性を軽減することもできます。または、例で使用したものよりも短い変数/メソッド名を使用することもできます。

于 2013-03-18T21:46:11.170 に答える
2

エイリアスを、同じ値を指す別のキー エントリにすることができます。

map.put("key1", object1);
map.put("aliaskey", map.get("key1")); // <== create alias

Object o = map.get("aliaskey"); // equals object1
于 2013-03-18T21:06:37.327 に答える
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;
    }

}
于 2013-03-18T21:07:51.460 に答える
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
于 2013-03-18T21:08:37.637 に答える