到目前为止,我一直在使用KeyedCollection来创建大量对象(接近1百万个),每个对象都有一个uint ID字段作为集合的键。这很好,但现在我在扩展功能时遇到了问题:我有“覆盖”记录,需要用新项目用相同的键替换任何现有项目。平均而言,20个记录中可能有1个可能被覆盖,可以想象甚至可以多次覆盖同一记录。
如果有必要,我不介意从KeyedCollection中重构。什么是我最好的选择?字典<&GT ;? id不是顺序的,所以直接索引就出来了。
答案 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;
}
}
}