Какова оптимальная структура данных для хранения объектов со строковым ключом и логическим вспомогательным значением?

Мне нужна структура данных, как показано ниже, но мне нужно иметь возможность изменить логическое значение. Два других остаются такими, какими они были при инициализации. Что бы вы использовали для лучшей производительности?

Dictionary<string, (object, bool)> dic = new Dictionary<string, (object, bool)>();

Я думал о хэш-таблице. Но хэш-таблица похожа на словарь с ключом/значением. Объект и логическое значение в моем примере по идее не похожи на ключ/значение, потому что другие значения внешнего словаря могут иметь тот же объект (или еще лучше... тип объекта). Я не хочу, чтобы кто-то, глядя на мой код позже, думал, что объект и логическое значение более связаны, чем они есть на самом деле.

Обновлено: object в этом примере — просто заполнитель. На самом деле это сложный объект с другими объектами в нем и так далее. Процедура, предшествующая этой, создает кучу этих объектов, и некоторые из них являются глубокими копиями других. Они передаются этой процедуре. Все объекты здесь названы по некоторым правилам и сохранены в словаре. Имена явно уникальны. Последующая процедура возьмет этот словарь и установит и выключит логическое значение на основе значений самих объектов и значений других логических значений. Процедура будет рекурсивной, пока не будет достигнуто некоторое состояние.

Количество объектов (или записей dic.) произвольно, но ожидается, что оно будет >100 && <500. Временная сложность O(n).

Я ориентируюсь на .NET7 (стандарт).

Зачем ты вообще используешь object?

Dai 18.02.2023 20:56

«Я думал о хэш-таблице. Но хэш-таблица похожа на словарь с ключом/значением». - Класс .NET Dictionary<K,V> представляет собой хеш-таблицу.

Dai 18.02.2023 20:57

Что представляют объект и логическое значение? Трудно предложить альтернативу, не понимая ваши варианты использования

Flydog57 18.02.2023 20:58

вам нужно иметь пользовательский объект в словаре, или вам нужно заменить все значение

pm100 18.02.2023 21:02

Слово предупреждения никогда не хэширует класс без специально реализованного метода .GetHashCode, основанного на неизменном ключе.

Strom 19.02.2023 03:02

@Стром, почему бы и нет? В чем проблема с HashSet<MyClass>, где MyClass не переопределяет метод GetHashCode?

Theodor Zoulias 22.02.2023 10:19

@TheodorZoulias, Hash позволит добавить две или более копий одного и того же объекта и не сможет их различить. Это также продлевает время жизни объекта до тех пор, пока не будут удалены все ссылки.

Strom 23.02.2023 04:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
113
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

но мне нужно иметь возможность изменить логическое значение.

Вы можете просто переназначить значение для ключа:

var tuples = new Dictionary<string, (object Obj, bool Bool)>
{
    { "1", (new object(), true) }
};
tuples["1"] = (tuples["1"].Obj, false); // or tuples["1"] = (tuples["1"].Item1, false);

Или

if (tuples.TryGetValue("1", out var c))
{
    tuples["1"] = (c.Obj, false);
}

Лично я бы остановился на этом, но для сценариев с действительно высокой производительностью вы можете посмотреть CollectionMarshall вместо второго фрагмента:

ref var v = ref CollectionsMarshal.GetValueRefOrNullRef(tuples, "1");
if (!Unsafe.IsNullRef(ref v))
{
    v.Bool = false;
}

Чуть больше информации - здесь.

Гуру Строн Я подумал, что кто-то должен сделать пакет NuGet, который позволит более легко использовать API CollectionsMarshal ! Например: if (tuples.TryGetValueRef("1", ref var v)) v.Bool = false; В некоторых классах уже есть хорошие вещи, такие как LinkedListNode<T>.ValueRef.

Theodor Zoulias 19.02.2023 04:14
Ответ принят как подходящий

Для аспекта «производительности»:

Словарь .NET использует хэши для поиска нужного элемента, что очень быстро (сравнимо с HashTable). Я не ожидаю больших проблем с производительностью, связанных с этим, или, по крайней мере, ничего, что можно было бы улучшить с другими структурами данных.

Кроме того, вы не должны беспокоиться о производительности, если только вы не делаете что-то миллион раз подряд + оказывается (на практике), что что-то занимает измеримое количество времени.

Для аспекта «изменение логического значения»:

... это довольно длинная история.

В .NET есть 2 варианта кортежа:

  • Кортеж значений, созданный выполнением var x = (myObj, myBool), как вы делаете.
    x — это структура и, следовательно, тип значения. На самом деле вы можете просто изменить x.Item1 или x.Item2 на новое значение.
    Однако... если вы поместите x в словарь, вы фактически поместите копию x (с копией его значений) в словарь, потому что такова природа типов значений. Когда вы снова извлекаете его из Словаря, создается еще одна копия, что делает невозможным изменение фактического кортежа внутри Словаря; любая попытка сделать это приведет только к изменению последней копии, которую вы получили.
    Побочная история: компилятор .NET знает об этом, поэтому он отказывается компилировать такой код, как dic[yourKey].Item2 = newBool;, потому что такой код не будет делать то, на что вы могли бы надеяться. Вы в основном говорите компилятору создать копию, изменить копию, а затем... отказаться от копии. Компилятору требуется переменная для хранения копии до того, как остальные смогут запуститься, но мы не предоставили никакой переменной.

  • Универсальный класс Tuple, а точнее набор универсальных классов, экземпляр которых можно создать с помощью вызовов типа var x = Tuple.Create(myObj, myBool). Однако эти классы запрещают изменять какие-либо их свойства, они всегда доступны только для чтения. Экземпляры класса Tuple можно поместить в словарь, но они все равно будут доступны только для чтения.

Итак, какие варианты действительно существуют для «изменения значения в кортеже» в словаре?

  1. Продолжайте использовать кортеж значений, но смиритесь с тем, что для «изменения» кортежа в словаре вам придется создать новый экземпляр (либо копию, либо с нуля), установить для него нужные свойства и поместить этот экземпляр (или фактически копию...) в словарь:

    // initialize it
    var dict = new Dictionary<string, (object, bool)>();
    var obj = new object();
    dict["abc"] = (obj, true);
    
    // change it
    var tmpTuple = dict["abc"]; // get copy
    tmpTuple.Item2 = false;   // alter copy
    dict["abc"] = tmpTuple;   // store another copy
    
    // or if you want to avoid the tmp variable
    dict["abc"] = (dict["abc"].Item1, false)
    
  2. Используйте собственный класс вместо кортежа значений или класса Tuple, а затем поместите его в словарь:

    public class MyPair
    {
        public object O { get; set; }
        public bool B { get; set; }
    }
    
    // initialize it
    var dict = new Dictionary<string, MyPair>();
    var obj = new object();
    dict["abc"] = new MyPair { O = obj, B = true };
    
    // change it
    dict["abc"].B = false;
    

Таким образом, оба типа кортежей подходят для объектов, с которыми вы не хотите многого делать. Но у обоих есть определенные ограничения в их использовании, и рано или поздно вам может понадобиться начать использовать классы.

Другие вопросы по теме