带可更换物品的钥匙收集

时间:2012-11-26 08:09:57

标签: c# collections

到目前为止,我一直在使用KeyedCollection来创建大量对象(接近1百万个),每个对象都有一个uint ID字段作为集合的键。这很好,但现在我在扩展功能时遇到了问题:我有“覆盖”记录,需要用新项目用相同的键替换任何现有项目。平均而言,20个记录中可能有1个可能被覆盖,可以想象甚至可以多次覆盖同一记录。

如果有必要,我不介意从KeyedCollection中重构。什么是我最好的选择?字典<&GT ;? id不是顺序的,所以直接索引就出来了。

3 个答案:

答案 0 :(得分:4)

这是一个老问题,我之前已经使用过现有的答案。但是在玩KeyedCollection<>时我再一次意识到,删除然后添加技术的效率低于我现在实施的技术。问题是Remove()方法线性搜索List,然后将列表的其余部分向左移动一个条目。我在这里介绍的技术也可以线性搜索List,但至少它可以避免移动List的其余部分。

NB。这仅适用于替换项目与要替换的项目具有相同密钥的情况。

   /// <summary>
   /// Derived (but still abstract) version of KeyedCollection{} to provide a couple of extra 
   /// services, in particular AddOrReplace() and Replace() methods.
   /// </summary>
   public abstract class KeyedList<TKey, TItem> : KeyedCollection<TKey, TItem>
   {
      /// <summary>
      /// Property to provide access to the "hidden" List{} in the base class.
      /// </summary>
      public List<TItem> BaseList
      {
         get { return base.Items as List<TItem>; }
      }


      /// <summary>
      /// Method to add a new object to the collection, or to replace an existing one if there is 
      /// already an object with the same key in the collection.
      /// </summary>
      public void AddOrReplace(TItem newObject)
      {
         int i = GetItemIndex(newObject);
         if (i != -1)
            base.SetItem(i, newObject);
         else
            base.Add(newObject);
      }


      /// <summary>
      /// Method to replace an existing object in the collection, i.e., an object with the same key. 
      /// An exception is thrown if there is no existing object with the same key.
      /// </summary>
      public void Replace(TItem newObject)
      {
         int i = GetItemIndex(newObject);
         if (i != -1)
            base.SetItem(i, newObject);
         else
            throw new Exception("Object to be replaced not found in collection.");
      }


      /// <summary>
      /// Method to get the index into the List{} in the base collection for an item that may or may 
      /// not be in the collection. Returns -1 if not found.
      /// </summary>
      private int GetItemIndex(TItem itemToFind)
      {
         TKey keyToFind = GetKeyForItem(itemToFind);
         return BaseList.FindIndex((TItem existingItem) => 
                                   GetKeyForItem(existingItem).Equals(keyToFind));
      }
   }

答案 1 :(得分:3)

Dictionary一样,您应该从集合中删除该项目,更改/替换它并再次添加。

答案 2 :(得分:0)

我使用以下类作为可更新的键控集合。它仅适用于引用类型。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using JetBrains.Annotations;


namespace Fiftytwo
{
    [PublicAPI]
    public abstract class UpdatableKeyedCollection<TKey, TValue> : KeyedCollection<TKey, TValue> where TValue : class
    {
        public List<TValue> ItemList => ( List<TValue> )Items;

        public bool AddOrUpdate ( [NotNull] TValue item )
        {
            int i, count = Count;
            if( count == 0 )
            {
                Add( item );
                return true;
            }

            List<TValue> list;

            TKey key = GetKeyForItem( item );
            var dict = Dictionary;
            IEqualityComparer<TKey> comparer;
            if( dict == null )
            {
                comparer = Comparer;
                list = ( List<TValue> )Items;
                for ( i = 0; i < count; ++i )
                {
                    if( comparer.Equals( GetKeyForItem( list[i] ), key ) )
                    {
                        list[i] = item;
                        return false;
                    }
                }
                Add( item );
                return true;
            }

            if( dict.TryGetValue( key, out TValue obj ) )
            {
                if( obj == item )
                    return false;

                comparer = Comparer;
                list = ( List<TValue> )Items;
                for ( i = 0; i < count; ++i )
                {
                    if( comparer.Equals( GetKeyForItem( list[i] ), key ) )
                    {
                        list[i] = item;
                        dict[key] = item;
                        return false;
                    }
                }

                throw new KeyNotFoundException(
                    $"Key {key} does not exist in the internal list but exists in the internal lookup dictionary." );
            }

            Add( item );
            return true;
        }

        [NotNull] public TValue GetOrAddNew ( [NotNull] TKey key, [NotNull] Func<TKey, TValue> createNew )
        {
            TValue value;

            int count = Count;
            if( count == 0 )
            {
                value = createNew( key );
                Add( value );
                return value;
            }

            var dict = Dictionary;
            if( dict == null )
            {
                var comparer = Comparer;
                var list = ( List<TValue> )Items;
                for ( int i = 0; i < count; ++i )
                {
                    if( comparer.Equals( GetKeyForItem( list[i] ), key ) )
                        return list[i];
                }
                value = createNew( key );
                Add( value );
                return value;
            }

            if( dict.TryGetValue( key, out value ) )
                return value;

            value = createNew( key );
            Add( value );
            return value;
        }
    }
}