Мне нужна структура данных, как показано ниже, но мне нужно иметь возможность изменить логическое значение. Два других остаются такими, какими они были при инициализации. Что бы вы использовали для лучшей производительности?
Dictionary<string, (object, bool)> dic = new Dictionary<string, (object, bool)>();
Я думал о хэш-таблице. Но хэш-таблица похожа на словарь с ключом/значением. Объект и логическое значение в моем примере по идее не похожи на ключ/значение, потому что другие значения внешнего словаря могут иметь тот же объект (или еще лучше... тип объекта). Я не хочу, чтобы кто-то, глядя на мой код позже, думал, что объект и логическое значение более связаны, чем они есть на самом деле.
Обновлено: object
в этом примере — просто заполнитель. На самом деле это сложный объект с другими объектами в нем и так далее. Процедура, предшествующая этой, создает кучу этих объектов, и некоторые из них являются глубокими копиями других. Они передаются этой процедуре. Все объекты здесь названы по некоторым правилам и сохранены в словаре. Имена явно уникальны. Последующая процедура возьмет этот словарь и установит и выключит логическое значение на основе значений самих объектов и значений других логических значений. Процедура будет рекурсивной, пока не будет достигнуто некоторое состояние.
Количество объектов (или записей dic.) произвольно, но ожидается, что оно будет >100 && <500. Временная сложность O(n).
Я ориентируюсь на .NET7 (стандарт).
«Я думал о хэш-таблице. Но хэш-таблица похожа на словарь с ключом/значением». - Класс .NET Dictionary<K,V>
представляет собой хеш-таблицу.
Что представляют объект и логическое значение? Трудно предложить альтернативу, не понимая ваши варианты использования
вам нужно иметь пользовательский объект в словаре, или вам нужно заменить все значение
Слово предупреждения никогда не хэширует класс без специально реализованного метода .GetHashCode
, основанного на неизменном ключе.
@Стром, почему бы и нет? В чем проблема с HashSet<MyClass>
, где MyClass
не переопределяет метод GetHashCode
?
@TheodorZoulias, Hash позволит добавить две или более копий одного и того же объекта и не сможет их различить. Это также продлевает время жизни объекта до тех пор, пока не будут удалены все ссылки.
но мне нужно иметь возможность изменить логическое значение.
Вы можете просто переназначить значение для ключа:
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.
Для аспекта «производительности»:
Словарь .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 можно поместить в словарь, но они все равно будут доступны только для чтения.
Итак, какие варианты действительно существуют для «изменения значения в кортеже» в словаре?
Продолжайте использовать кортеж значений, но смиритесь с тем, что для «изменения» кортежа в словаре вам придется создать новый экземпляр (либо копию, либо с нуля), установить для него нужные свойства и поместить этот экземпляр (или фактически копию...) в словарь:
// 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)
Используйте собственный класс вместо кортежа значений или класса 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;
Таким образом, оба типа кортежей подходят для объектов, с которыми вы не хотите многого делать. Но у обоих есть определенные ограничения в их использовании, и рано или поздно вам может понадобиться начать использовать классы.
Зачем ты вообще используешь
object
?