2012-05-03 55 views
3

Есть ли способ прямого доступа к исходному ключевому объекту из словаря C# (где ключ является ссылочным типом)? Я видел этот вопрос на другом сайте (http://www.pcreview.co.uk/forums/get-original-key-object-dictionary-t3351718.html), и поскольку большинство респондентов не понимали, почему это разумный вопрос/не является симптомом плохого базового дизайна, я подробно расскажу ниже.Извлечь исходный ключ из словаря

В обходных у меня до сих пор являются:

(а) перебрать ключи проверки равенства, что О (п) сложность :(

(б) поддерживать дополнительный пул стажера (например, другой словарь, который отображает ключи для себя). Это приводит нас к сложности O (1), но требует дополнительной памяти, дополнительной работы, когда элементы добавляются в словарь, риск неспособности получить эту координацию правильно ... ничто из этого не является фатальным но он не идеален и не понадобится, если словарь <> предложил метод извлечения ключа ключом.

В частности, у меня есть словарь < Состояние, список < Состояние >>, которое представляет граф состояний: каждый ключ представляет состояние конечного автомата, а связанное с ним значение представляет собой список соседних состояний, которые можно достичь непосредственно оттуда. например государство может представлять конфигурацию шахматной доски, а соседние состояния - это конфигурации, достижимые за один ход.

Я заполняю граф состояний, начиная с исходного состояния, строя список соседей, а затем рекурсивно вызывая подпрограмму народонаселения для каждого из этих соседей. На каждом этапе алгоритм проверяет, является ли соседнее состояние уже ключевым в словаре (иначе мы никогда не закончим). Состояние - это ссылочный тип (то есть класс) с переопределенными методами GetHashCode и Equals.

Семантически это работает нормально. Но теперь списки соседей - это разные копии состояний, а не оригинальная копия, поэтому, если есть N состояний со средним числом соседей M каждый, у нас есть объекты состояния NxM в памяти, когда нам действительно нужны только N объектов State и 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

Полностью согласен. ОП говорит, что это проблема словаря, но его дизайн требует, чтобы ссылочное равенство не только равнялось равенству.Если вы этого хотите, тогда вам нужно реализовать его, а не требовать от словаря возврата ключа на основе предоставленного ключа;) – empi

+0

Thx Yossarian. Что-то в этом роде было тем, что я называл обходным путем (б) в своем посте. Я/надеялся, что у кого-то есть решение, которое ближе к эффективности, которую вы получите, если бы MS предоставил прямой доступ. В общем, если вы хотите использовать пул-пул, вам нужно сделать что-то вроде этого, и это нормально. Но если ваши объекты уже являются ключами в словаре, вы должны получить эту функциональность бесплатно. Поскольку словарь не раскрывает исходные ключи (кроме свойства Keys), похоже, что это невозможно. – topos

+0

Проблема в том, что '_samples [s]' не будет работать. –

Смежные вопросы