如果值是对象并且这些对象的属性是键,是否有比字典更好的数据结构?

发布于 2024-08-19 15:26:26 字数 490 浏览 7 评论 0原文

我有一个 Dictionary ,其中 intobj 的属性。有没有更好的数据结构呢?我觉得使用属性作为钥匙是多余的。

Dictionary 是容器类中的一个字段,允许根据 int id 数字对 obj 值进行随机索引。容器类中的简化(无异常处理)索引器如下所示:

obj this[int id]
{
     get{ return this.myDictionary[id];}
}

其中 myDictionary 是前面提到的保存对象的 Dictionary

这可能是快速随机访问的典型方式,但我想获得第二意见。

I have a Dictionary<int, object> where the int is a property of obj. Is there a better data structure for this? I feel like using a property as the key is redundant.

This Dictionary<int, obj> is a field in a container class that allows for random indexing into the obj values based on an int id number. The simplified (no exception handling) indexer in the container class would look like:

obj this[int id]
{
     get{ return this.myDictionary[id];}
}

where myDictionary is the aforementioned Dictionary<int, obj> holding the objects.

This may be the typical way of quick random access but I wanted to get second opinions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

鲜血染红嫁衣 2024-08-26 15:26:26

框架中没有具体的类可以执行此操作。不过有一个抽象的,KeyedCollection。您必须从该类派生您自己的类并实现 GetKeyForItem() 方法。这非常简单,只需返回您想要索引的属性的值即可。

这就是您需要做的全部,但请密切关注 ChangeItemKey()。当用作键的属性更改值时,您必须执行一些有意义的操作。如果您确保该属性是不可变的(只有一个 getter),那么就很容易了。但如果你不这样做,那就很尴尬了,对象本身现在需要知道它存储在你的集合中。如果您不采取任何措施(调用 ChangeItemKey),该对象就会在集合中丢失,您将无法找回它。非常接近泄漏。

请注意 Dictionary<> 是如何显示的通过分别指定键值和对象来回避这个问题。您可能仍然无法找回该对象,但至少它不会因设计而丢失。

There's no concrete class in the framework that does this. There's an abstract one though, KeyedCollection. You'll have to derive your own class from that one and implement the GetKeyForItem() method. That's pretty easy, just return the value of the property by which you want to index.

That's all you need to do, but do keep an eye on ChangeItemKey(). You have to do something meaningful when the property that you use as the key changes value. Easy enough if you ensure that the property is immutable (only has a getter). But quite awkward when you don't, the object itself now needs to have awareness of it being stored in your collection. If you don't do anything about it (calling ChangeItemKey), the object gets lost in the collection, you can't find it back. Pretty close to a leak.

Note how Dictionary<> side-steps this problem by specifying the key value and the object separately. You may still not be able to find the object back but at least it doesn't get lost by design.

謌踐踏愛綪 2024-08-26 15:26:26

有一个 KeyedCollection 类。

编辑:KeyedCollection 可以在内部使用字典,但对于这种特定场景,它的界面比原始字典更干净,因为您可以直接按值查找。诚然,我觉得它总体上不是很有用。

There is a KeyedCollection class.

EDIT: The KeyedCollection can use a dictionary internally, but it cleaner interface for this particular scenario than a raw dictionary since you can lookup by values directly. Admittedly I don't find it very useful in general.

紫﹏色ふ单纯 2024-08-26 15:26:26

如果工厂设置带来的额外开销不值得,您可以轻松实现自己的 KeyedCollectionSystem.Collections.ObjectModel 中的原始 KeyedCollection 内部是一个 Dictionary 和一个 List code> 这意味着您可以在 IList<>IDictionary<> 上定义操作。例如,您可以插入、按索引访问、按插入顺序遍历集合(所有这些 IList<> 都很方便),同时您可以基于键进行快速查找(借助字典)。这意味着,当您添加或删除项目时,除了保存额外的 List 所需的少量内存开销之外,还必须在两个基础集合上执行这些项目(但对象不会重复)像这样)。虽然添加速度不会受到太大影响(List>> 添加是 O(1)),删除速度受到一点影响。

如果你不关心插入顺序和按索引访问:

public class KeyedCollection<TKey, TItem> : ICollection<TItem>
{
    MemberInfo _keyInfo;
    Func<TItem, TKey> _keySelector;
    Dictionary<TKey, TItem> _dict;

    public TItem this[TKey key]
    {
        get { return _dict[key]; }
    }

    public int Count
    {
        get { return _dict.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public ICollection<TKey> Keys
    {
        get { return _dict.Keys; }
    }

    private ICollection<TItem> Items
    {
        get { return _dict.Values; }
    }

    public KeyedCollection(Expression<Func<TItem, TKey>> keySelector, IEqualityComparer<TKey> comparer = null)
    {
        var keyExpression = keySelector.Body as MemberExpression;
        if (keyExpression != null)
            _keyInfo = keyExpression.Member;

        _keySelector = keySelector.Compile();
        _dict = new Dictionary<TKey, TItem>(comparer);
    }



    private TKey GetKeyForItem(TItem item)
    {
        return _keySelector(item);
    }

    public bool ContainsKey(TKey key)
    {
        return _dict.ContainsKey(key);
    }

    public bool Contains(TItem item)
    {
        return ContainsKey(GetKeyForItem(item));
    }

    public bool TryGetItem(TKey key, out TItem item)
    {
        return _dict.TryGetValue(key, out item);
    }

    public void Add(TItem item)
    {
        _dict.Add(GetKeyForItem(item), item);
    }

    public void AddOrUpdate(TItem item)
    {
        _dict[GetKeyForItem(item)] = item;
    }

    public bool UpdateKey(TKey oldKey, TKey newKey)
    {
        TItem oldItem;
        if (_keyInfo == null || !TryGetItem(oldKey, out oldItem) || !SetItem(oldItem, newKey))   // important
            return false;

        RemoveKey(oldKey);
        Add(oldItem);
        return true;
    }

    private bool SetItem(TItem item, TKey key)
    {
        var propertyInfo = _keyInfo as PropertyInfo;
        if (propertyInfo != null)
        {
            if (!propertyInfo.CanWrite)
                return false;

            propertyInfo.SetValue(item, key, null);
            return true;
        }

        var fieldInfo = _keyInfo as FieldInfo;
        if (fieldInfo != null)
        {
            if (fieldInfo.IsInitOnly)
                return false;

            fieldInfo.SetValue(item, key);
            return true;
        }

        return false;
    }

    public bool RemoveKey(TKey key)
    {
        return _dict.Remove(key);
    }

    public bool Remove(TItem item)
    {
        return RemoveKey(GetKeyForItem(item));
    }

    public void Clear()
    {
        _dict.Clear();
    }

    public void CopyTo(TItem[] array, int arrayIndex)
    {
        Items.CopyTo(array, arrayIndex);
    }

    public IEnumerator<TItem> GetEnumerator()
    {
        return Items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

我已经实现了ICollection 使其更加符合标准 - 并且您还可以获得很好的集合初始值设定项语法! :)

示例用法:

var p1 = new Person { Name = "a" };
var p2 = new Person { Name = "b" };

var people = new KeyedCollection<string, Person>(p => p.Name) { p1, p2 };
// p1 == people["a"];
// p2 == people["b"];

You can implement your own KeyedCollection trivially if the extra overhead that comes with the factory settings isn't worth it. The original KeyedCollection in System.Collections.ObjectModel is internally a Dictionary<TKey, TItem> and a List<TItem> which means you can have operations defined on both IList<> and IDictionary<>. For e.g., you can insert, access by index, traverse collection in the inserted order (all which IList<> facilitates) and at the same time you can have quick lookups based on key (with the help of dictionary). This means that when you're adding or removing an item they have to be performed on both underlying collections, apart from the small memory overhead to hold the extra List<> (but the objects are not duplicated as such). Though the addition speeds are not affected much (List<> addition is O(1)), removal speed is affected a little.

If you don't care about insertion order and accessing by index:

public class KeyedCollection<TKey, TItem> : ICollection<TItem>
{
    MemberInfo _keyInfo;
    Func<TItem, TKey> _keySelector;
    Dictionary<TKey, TItem> _dict;

    public TItem this[TKey key]
    {
        get { return _dict[key]; }
    }

    public int Count
    {
        get { return _dict.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public ICollection<TKey> Keys
    {
        get { return _dict.Keys; }
    }

    private ICollection<TItem> Items
    {
        get { return _dict.Values; }
    }

    public KeyedCollection(Expression<Func<TItem, TKey>> keySelector, IEqualityComparer<TKey> comparer = null)
    {
        var keyExpression = keySelector.Body as MemberExpression;
        if (keyExpression != null)
            _keyInfo = keyExpression.Member;

        _keySelector = keySelector.Compile();
        _dict = new Dictionary<TKey, TItem>(comparer);
    }



    private TKey GetKeyForItem(TItem item)
    {
        return _keySelector(item);
    }

    public bool ContainsKey(TKey key)
    {
        return _dict.ContainsKey(key);
    }

    public bool Contains(TItem item)
    {
        return ContainsKey(GetKeyForItem(item));
    }

    public bool TryGetItem(TKey key, out TItem item)
    {
        return _dict.TryGetValue(key, out item);
    }

    public void Add(TItem item)
    {
        _dict.Add(GetKeyForItem(item), item);
    }

    public void AddOrUpdate(TItem item)
    {
        _dict[GetKeyForItem(item)] = item;
    }

    public bool UpdateKey(TKey oldKey, TKey newKey)
    {
        TItem oldItem;
        if (_keyInfo == null || !TryGetItem(oldKey, out oldItem) || !SetItem(oldItem, newKey))   // important
            return false;

        RemoveKey(oldKey);
        Add(oldItem);
        return true;
    }

    private bool SetItem(TItem item, TKey key)
    {
        var propertyInfo = _keyInfo as PropertyInfo;
        if (propertyInfo != null)
        {
            if (!propertyInfo.CanWrite)
                return false;

            propertyInfo.SetValue(item, key, null);
            return true;
        }

        var fieldInfo = _keyInfo as FieldInfo;
        if (fieldInfo != null)
        {
            if (fieldInfo.IsInitOnly)
                return false;

            fieldInfo.SetValue(item, key);
            return true;
        }

        return false;
    }

    public bool RemoveKey(TKey key)
    {
        return _dict.Remove(key);
    }

    public bool Remove(TItem item)
    {
        return RemoveKey(GetKeyForItem(item));
    }

    public void Clear()
    {
        _dict.Clear();
    }

    public void CopyTo(TItem[] array, int arrayIndex)
    {
        Items.CopyTo(array, arrayIndex);
    }

    public IEnumerator<TItem> GetEnumerator()
    {
        return Items.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

I have implemented ICollection<TItem> to make it more standard compliant - and also you get the nice collection initializer syntax! :)

A sample usage:

var p1 = new Person { Name = "a" };
var p2 = new Person { Name = "b" };

var people = new KeyedCollection<string, Person>(p => p.Name) { p1, p2 };
// p1 == people["a"];
// p2 == people["b"];
旧人九事 2024-08-26 15:26:26

C# 动态属性 帖子似乎表明使用字典是一种流行的选择。其他帖子建议使用 HashTable

字典与哈希表

C# dynamic properties post seems to show that using a Dictionary was a popular choice. The other posts suggest using a HashTable

Dictionary vs Hashtable

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文