2012-05-03 107 views
3

有没有一种方法可以直接从C#Dictionary(其中的密钥是引用类型)访问原始密钥对象?我在另一个网站(http://www.pcreview.co.uk/forums/get-original-key-object-dictionary-t3351718.html)上看到过这个问题,并且由于大多数响应者不明白为什么这是一个合理的问题/没有不良的底层设计的症状,所以我在下面给出了背景细节。从字典中检索原始密钥

我的解决方法,到目前为止有:

(a)中迭代键检查平等,这是O(n)的复杂性:(

(b)中维持的附加实习生池(例如另一个将键映射到自己的字典),这使我们获得了O(1)的复杂性,但需要额外的内存,当项目被添加到字典时需要额外的工作,没有得到这种协调的风险......这些都不是致命的但它并不理想,如果Dictionary <>提供了一种通过密钥检索密钥的方法,则不是必需的。具体来说,我有一个字典< State,List < State >>它代表一个状态图:每个键表示一个状态机的状态,相关的值是从那里直接可到达的邻居状态列表。例如国家可以代表国际象棋棋盘的配置,邻国可以是一次移动中可达到的配置。

我从初始状态开始填充状态图,构造邻居列表,然后递归调用每个邻居的总体例程。在每个阶段,算法检查邻居状态是否已经是字典中的一个关键字(否则我们永远不会完成)。 State是一个重写GetHashCode和Equals方法的引用类型(即一个类)。

语义上,这工作正常。但是现在邻居列表是状态的不同副本,而不是原始副本,所以如果有N个状态,每个状态平均有M个邻居,则我们在内存中有NxM个状态对象,当我们只需要N个状态对象和NxM对状态对象的引用。除了消耗内存占用量之外,它使得相等性测试变慢,因为您无法从Equals()中的引用等同快捷键中受益。

从评论看来问题不明确,所以这里是一些伪代码来说明它更好地:

public class StateGraphExample 
{ 
    public static void Main() 
    { 
     State initialState = State.GetInitialState(); 
     var graph = new Dictionary<State, List<State>>(); 
     BuildGraph(initialState, graph); 
    } 

    public static void BuildGraph(State state, Dictionary<State, List<State>> graph) 
    { 
     var neighbours = new List<State>(); 

     foreach (State.Move move in state.GetValidMoves()) 
      neighbours.Add(state.ApplyMove(move)); 

     graph[state] = neighbours; 

     foreach (State neighbour in neighbours) 
      if (!graph.ContainsKey(neighbour)) 
       BuildGraph(neighbour, graph); 
    } 

    public class State 
    { 
     //Lots of data members here... 

     public static State GetInitialState() 
     { /* Return State object representing initial state... */ } 

     public class Move 
     { /* Representation of a move that takes us from one State to another... */ } 

     public List<State.Move> GetValidMoves() 
     { /* Return valid moves from this State object... */ } 

     public State ApplyMove(State.Move move) 
     { /* Clones this State object, applies move to it and returns the result... */ } 

     public override int GetHashCode() 
     { /* Compute hash... */ } 

     public override bool Equals(object obj) 
     { /* Test equality... */ } 

     private State Clone() 
     { /* Clone self... */ } 
    } 
} 
+0

您确定字典的运算符[]具有O(1)复杂性吗? – nothrow

+0

为什么邻居列表是州的不同副本?如果'State'是一个类,那么你只有引用,每个列表只保存对相同对象的引用,或者你是否从头开始创建所有的邻居状态? – Oliver

+0

如果你需要一个只使用键的字典(因为你只需要知道关键字的唯一性),你应该使用'HashSet '而不是'Dictionary '。 – Oliver

回答

2

您可以创建一个State“池”,然后,当创建,如果已经创建了,则使用现有的。示例:

class Sample : IEquatable<Sample> 
{ 
    private int _params; 
    private Sample(int params) { _params = params; } 

    private static HashSet<Sample> _samples = new HashSet<Sample>(); 

    public static GetSample(int params) 
    { 
    Sample s = new Sample(params); 
    if (!_samples.ContainsKey(s)) 
     _samples.Add(s); 

    return _samples[s]; 
    } 
} 
+0

完全同意。 OP说这是一个Dictionary的问题,但是他的设计要求参考平等不仅仅等于平等。如果你想要它,那么你必须实现它,而不是要求从字典返回键基于提供的密钥;) – empi

+0

Thx Yossarian。沿着这些方向的东西就是我在文章中提到的解决方法(b)。如果MS提供了直接访问权限,我希望有人提供的解决方案更接近您将获得的效率。一般来说,如果你想要一个实习生池,你必须这样做,这很好。但是如果你的对象已经是字典中的关键字,你应该可以免费获得这个功能。既然Dictionary不公开原始键(除了通过Keys属性),看起来这是不可能的。 – topos

+0

这里的问题是'_samples [s]'不起作用。 –