Лучшая структура для доступа к объекту, зная уникальное свойство - PullRequest
1 голос
/ 03 октября 2019

У меня есть массив элементов, к которым мне нужно обращаться довольно часто и довольно быстро. Я знаю, что свойство этих элементов уникально, и я внедряю массив в класс-обертку, который строит словарь из этого свойства. Поскольку доступ к словарю намного быстрее, чем запрос LINQ к массиву, я подумал, что это будет эффективно.

Но, несмотря на то, что он работает хорошо, решение выглядит немного странно: я сохраняю объект какзначение в словарь, а часть его в качестве ключа.

Есть ли лучше подходящая структура / способ сделать это?

 public class FooItem
{

    public string Id { get; set; } //UNIQUE

    public double Value1 { get; set; }
    public string Value2 { get; set; }

}

public class FoosManager
{
    private Dictionary<string, FooItem> _dic;

    public FooManager(IEnumerable<FooItem> items) {
        _dic = new Dictionary<string, FooItem>(
            items.ToDictionary(x => x.Id, x => x),
            StringComparer.OrdinalIgnoreCase);
    }

    public FooItem this[string id] => GetItem(id); //Indexer

    private FooItem GetItem(string id) {
        if (_dic.ContainsKey(id)) 
            return null;

        return _dic[id];
    }

}

Ответы [ 2 ]

1 голос
/ 04 октября 2019

Использование строк в качестве ключей Dictionary, тех же строк, которые хранятся в свойстве Id, вполне допустимо, поскольку строки не копируются. Копируются только ссылки на строку (по моим тестам 8 байтов на ссылку). Если, конечно, вы не создадите явно копии, подобные этой:

var dictionary = items.ToDictionary(x => new String(x.Id.ToCharArray()));

В этом случае память, выделенная для строк, будет удвоена, хотя функциональность Dictionary останется неизменной.

1 голос
/ 03 октября 2019

Вы можете упростить ваш код до

public class FoosManager {
  private Dictionary<string, FooItem> _dic;

  public FooManager(IEnumerable<FooItem> items) {
    if (null == items)
      throw new ArgumentNullException(nameof(items));

    // Let's do it in one go:
    _dic = items.ToDictionary(
       item => item.Id, 
       item => item, 
       StringComparer.OrdinalIgnoreCase);
  }

  // No need in GetItem, ContainsKey etc.
  public FooItem this[string id] => 
    _dic.TryGetValue(id, out var value) ? value : null;
} 

Что касается FooItem, я бы сделал его неизменным (по крайней мере, Id; в противном случае можно поставить FooItemв FoosManager, а затем изменить Id)

  public class FooItem {
    public FooItem(string id, double value1, string value2) {
      if (null == id)
        throw new ArgumentNullException(nameof(id)); 

      Id = id;
      Value1 = value1;
      Value2 = value2;
    } 

    public string Id { get; } 

    public double Value1 { get; }
    public string Value2 { get; }
  } 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...