Pattern或antipattern:在Dictionary / Hashmap中使用value属性作为键

时间:2012-03-23 12:00:33

标签: design-patterns dictionary hashmap anti-patterns

我想知道在Hashmap中使用值的属性作为键是否是一个好习惯。

在Java中看起来像:

private HashMap<String, VariableCondition> m_conditions = new HashMap<String, VariableCondition>();

// ...
m_conditions.put(var, new VariableCondition(var, value));

如果可以从对象外部访问HashMap,那将至关重要。但即使作为成员变量,您也必须关心HashMap自己的一致性。

是否有另一种模式来实现用例。使用List是可能的,但您必须为每次访问迭代整个列表。

4 个答案:

答案 0 :(得分:1)

在.NET中,有KeyedCollection可以完全满足您的需求。

但一般来说,关键和价值之间的联系是一种应该隐藏在消费者之间的内部知识。所以我建议将该哈希封装在其他类中,该类将提供API来设置/获取数据。以下是C#中的快速示例:

public class KeyedStorage<K, T>
{
    private readonly Func<T, K> keyFunc;
    private readonly Dictionary<K, T> dictionary = new Dictionary<K, T>();

    public KeyedStorage(Func<T, K> keyFunc)
    {
        this.keyFunc = keyFunc;
    }

    public void Add(T item)
    {
        var key = keyFunc(item);
        dictionary.Add(key, item);
    }

    public T this[K key]
    {
        get { return dictionary[key]; }
    }
}

public class KeyedStorageTests
{
    [Fact]
    public void should_return_item_after_add()
    {
        // arrange
        var keyedStorage = new KeyedStorage<int, string>(s => s.GetHashCode());

        // act
        keyedStorage.Add("werew");

        // assert
        keyedStorage["werew".GetHashCode()]
            .Should()
            .Be("werew");
    }
}

答案 1 :(得分:1)

您所描述的内容就像在集合上构建索引一样。我一般都没有看到任何问题。请确保密钥是唯一的!

如果你使用java,你可能会考虑guava的Maps类的uniqueIndex方法。

答案 2 :(得分:1)

也许有更漂亮的方法,但我使用这一点,特别是在原型设计期间。

收集东西:

一个例子 - 非常接近你的例子 - 是你有一堆日期,你想获得每天的列表。

List<Event> events = dearDataStorePlzGiveMeDates(); 
TreeMap<Date, List<Event>> byDay = new TreeMap<>(); 
for( Event e : events ){
    List<Event> forDay = byDay.get( e.getDate() ); 
    if( forDay == null ) byDay.put( forDay = new List<Event>() ); 

    forDay.add( e ); 
}
如果你有一个沉闷的数据存储和少量的数据,这是非常好的。 我正在使用树图而不是哈希图,因为它们在键上有一个排序。 我也有时将内部列表类型改为某些排序数据结构。

请注意,在函数式语言中这会完全不同(将事件映射到日期,排序,然后为每个日期收集事件;非常慢,但是1-liner)。

作为集合的脏替换

另一件事,如果经常需要的是通过一个疯狂的标准使一些数据结构独特,同时仍然保留一个关联数据的实例。考虑一下:

List<Creatures> = ...; // from somewhere
HashTable<Integer, Creature> examples = new HashTable<>(); 

for( Create c : creatures ){
    examples.put( c.getNumberLegs(), c ); 
}

现在这会让你成为一条腿,一条腿有两条腿的动物等等。

全部测试

我根本不介意这些结构,如果你对你的意图做一点评论,他们应该很容易理解。

如果你必须处理现有数据,我发现它们特别有用,即你无法访问数据存储区(如mysql),或者数据存储区非常枯燥,不知道如何完成这些任务。

hashmaps的一大优势是它们的复杂性,它们具有O(1)访问时间(树图的O(log n)),这是非常好的。

替代方案

听起来你真正想要的是一个“索引”,一种基于其中一个属性快速访问实例的方法。 在java中有TreeSet类。它具有所有操作的O(log n)复杂度,并使用如下:

TreeSet<MyClass> indexed = new TreeSet<>( new Comparator<MyClass>(){
    public int compare( MyClass a, MyClass b ){
        // "sort" by the property (assuming they are Integers)
        return a.getThing().compareTo( b.getThing() ); 
    }
}); 

indexed.addAll( theList ); 
但注意:(1)这里的行为与你的例子不同!! 假设密钥出现多次,在您的代码中(使用哈希表),最后一个副本将存活。 在这种情况下,第一个元素将存活(树形图基本上说“这已经在那里,所以......无论如何”) (2)运行时复杂度很好,但在使用HashMaps时比O(1)运行时更差。

希望这能回答你的问题...

答案 3 :(得分:1)

如果你的变量只有一个条件值,那就非常好了,但是IMO更容易拥有map,因为pair {variable,value}它本身就是{key,value}对,所以你不需要VariableCondition抽象。