在c#dictionary中仅使用一次查找查找查找或插入

在c#dictionary中仅使用一次查找查找查找或插入,c#,performance,stl,comparison,lookup,C#,Performance,Stl,Comparison,Lookup,我以前是一名C++/STL程序员,试图用C#/.NET技术编写一个快速行进算法 我正在搜索与STL方法“map::insert”等价的方法,该方法在给定的键处插入一个值,如果不存在,则向现有的键-值对返回一个迭代器 我找到的唯一方法是通过两个查找来实现这一点:一个在TryGetValue内部,另一个在Add方法中: List<Point> list; if (!_dictionary.TryGetValue (pcost, out list)) { list = new Li

我以前是一名C++/STL程序员,试图用C#/.NET技术编写一个快速行进算法

我正在搜索与STL方法“map::insert”等价的方法,该方法在给定的键处插入一个值,如果不存在,则向现有的键-值对返回一个迭代器

我找到的唯一方法是通过两个查找来实现这一点:一个在TryGetValue内部,另一个在Add方法中:

List<Point> list;
if (!_dictionary.TryGetValue (pcost, out list))
{
    list = new List<Point> ();
    dictionary.Add (pcost, list);
}
list.Add (new Point { X = n.x, Y = n.y });
列表;
if(!\u dictionary.TryGetValue(pcost,out list))
{
列表=新列表();
添加(pcost,list);
}
添加(新点{X=n.X,Y=n.Y});
有什么东西可以解释为什么使用.NET容器不可能做到这一点?还是我错过了一些要点


谢谢。

您可以为此创建扩展方法:

IDictionary<string, Point> _dictionary = GetDictionary();
_dictionary.GetOrAdd( "asdf" ).Add( new Point(14, 15) );

// ... elsewhere ...
public static class DictionaryExtensions {
    public static List<TValue> GetOrAdd<TKey, TValue>( this IDictionary<TKey, List<TValue>> self, TKey key ) {
        List<TValue> result;
        self.TryGetValue( key, out result );
        if ( null == result ) {
            // the key value can be set to the null
            result = new List<TValue>();
            self[key] = result;
        }

        return result;
    }
}
IDictionary\u dictionary=GetDictionary();
_添加(新的点(14,15));
// ... 在别处
公共静态类字典扩展{
公共静态列表GetOrAdd(此IDictionary self,TKey){
列出结果;
self.TryGetValue(键,输出结果);
if(null==结果){
//键值可以设置为null
结果=新列表();
自[键]=结果;
}
返回结果;
}
}

您可以通过以下方式分配您的值:

var dict = new Dictionary<int, int>();
dict[2] = 11;
var dict=newdictionary();
dict[2]=11;
如果键为2的值不存在-将添加该值,否则将覆盖该值

Dictionary没有方法GetOrAdd,但C#4.0中的ConcurrentDictionary有:

var dict = new ConcurrentDictionary<int, int>();
dict[2] = 10;
int a = dict.GetOrAdd(2, 11);// a == 10
var dict=新的ConcurrentDictionary();
dict[2]=10;
int a=dict.GetOrAdd(2,11);//a==10

标准通用词典不支持此功能,需要进行两次查找。虽然查找的成本通常可以忽略不计,所以这不是一个问题,而且您通常可以通过调整系统的其他部分而不是尝试微观优化字典查找来获得更好的结果

据我所知,.net附带的唯一一个支持此功能的词典是with The方法。虽然现在你要支付同步的费用

有什么可以解释为什么吗 这在使用.NET时是不可能的 集装箱


在不知道真实背景的情况下,我认为这是因为字典的简单性。只有一些基本的、易于理解的函数:
Add
Remove
a.s.o.,而索引操作符却有一点魔力,这可能被认为是直观的。

不幸的是,bcl的实现中没有一个。最接近的方法是进行两次查找,但其中一种方法可以有一个通用的扩展方法来简化查找

我不知道他们为什么不接受
Func
而不是
V
来延迟对象创建。C5有很多类似的技巧,例如

public virtual bool Remove(K key, out V value)

public virtual bool Update(K key, V value, out V oldvalue)

public virtual bool UpdateOrAdd(K key, V value, out V oldvalue)

老问题,但我可能刚刚偶然发现了一个可接受的解决方案。我使用TryGetValue、三元运算符和索引赋值的组合

var thing=\u dictionary.TryGetValue(key,out-var-existing)?现有:_dictionary[key]=新事物();
我为此写了一个小例子

类程序
{
专用静态只读字典\u翻译
=新字典(){{“en”,“你好,世界!”};
私有静态字符串AddOrGetTranslation(字符串区域设置、字符串defaultText)
=>\u translations.TryGetValue(区域设置,out var existingTranslation)
?现有翻译
:_translations[locale]=默认文本;
静态void Main()
{
var defaultText=“#你好,世界#”;
Console.WriteLine(AddOrGetTranslation(“en”,defaultText));//->你好,世界!
Console.WriteLine(AddOrGetTranslation(“de”,defaultText));//->#hello world#
Console.WriteLine(AddOrGetTranslation(“de”,“differentitdefaulttext”);//->#你好,世界#
_翻译[“de”]=“你好!”;
Console.WriteLine(AddOrGetTranslation(“de”,defaultText));//->你好!
}
}

您确定即使在生产代码中也会进行两次查找吗?双重查找真的重要吗?时间上的差别是微乎其微的。@Chris:什么?您没有任何基准来支持这一点,更不用说与OP的使用模式相关的基准;)--我可以在重要的地方向您展示代码(哦,等等,出于法律原因,我不能…)事实上,我还没有进行基准测试:可能cli机器会缓存最后一个结果作为下次查找的提示,这可能会抑制添加操作的成本。您假设TValue是其中的引用类型,您应该检查TryGetValue的返回值。这与问题中的代码相同,只是现在做了一个扩展。这有什么帮助?还有两个调用,trygetvalue和add。@PowerRoy:这是唯一的方法,如何使用Dictionary实现它。如果您愿意,您可以自己实现IDictionary并以更高效的方式实现。TryGetValue有一个返回值。检查空值的结果无效,您可能已将空值添加为值。将空值添加为值不会导致异常,仅将空值用作键。但是,TryGetValue有一个返回值,必须对该返回值求值,以使代码可靠。@gorik使用如您所示的字典,如果该键不存在,将抛出异常exist@amichaigerstl否,请试用第一个是“添加或更新”+1表示“GetOrAdd”。这并不能解决问题,在我的例子中,字典的值是一个列表,我不想丢失已经为特定键添加的元素。false,请检查此答案:只有一个查找。@v.oddou对
dict.Add
的调用在内部执行查找,因此执行了两个查找。一个是通过TryGetValue查找,一个是通过Add查找。这个解决方案仍然使用两个查找,或者我遗漏了什么?
public virtual bool FindOrAdd(K key, ref V value)
{

}
public virtual bool Remove(K key, out V value)

public virtual bool Update(K key, V value, out V oldvalue)

public virtual bool UpdateOrAdd(K key, V value, out V oldvalue)