¿Quieres aprender a usar TDictionary para tablas de Hash en Delphi?

Introducida en Delphi 2009, la clase TDictionary, definida en la unidad Generics.collections, representa una colección genérica de pares clave-valor.

 

Los tipos genéricos, también introducidos en Delphi 2009, permiten definir clases que no definen específicamente el tipo de miembros de datos.

 

Un diccionario es, en cierto modo, similar a una matriz. En un array se trabaja con una serie (colección) de valores indexados por un valor entero, que puede ser cualquier valor de tipo ordinal. Este índice tiene un límite inferior y uno superior.

 

En un diccionario se pueden almacenar claves y valores donde cualquiera de los dos puede ser de cualquier tipo.

El constructor de TDictionary

De ahí la declaración del constructor de TDictionary:

 

TDictionary<TKey, TValue>.Create;

 

En Delphi, el TDictionary se define como una tabla hash. Las tablas Hash representan una colección de pares clave-y-valor que se organizan en base al código hash de la clave. Las tablas Hash están optimizadas para búsquedas (velocidad). Cuando se añade un par clave-valor a una tabla hash, el hash de la clave se calcula y almacena junto con el par añadido.

 

Los TKey y TValue, por ser genéricos, pueden ser de cualquier tipo. Por ejemplo, si la información que va a almacenar en el diccionario proviene de alguna base de datos, su Clave puede ser un valor GUID (o algún otro valor que presente el índice único) mientras que el Valor puede ser un objeto asignado a una fila de datos en las tablas de su base de datos.

Uso de TDictionary

En aras de la simplicidad, el siguiente ejemplo utiliza números enteros para TKeys y caracteres para TValues.

 

// // "log" is a TMemo control placed on a form //vardict : TDictionary<integer, char>;sortedDictKeys : TList<integer>;i, rnd : integer;c : char;beginlog.Clear;log.Text := 'TDictionary usage samples';Randomize;dict := TDictionary<integer, char>.Create;try//add some key/value pairs (random integers, random characters from A in ASCII)for i := 1 to 20 dobeginrnd := Random(30);if NOT dict.ContainsKey(rnd) then dict.Add(rnd, Char(65 + rnd));end;//remove some key/value pairs (random integers, random characters from A in ASCII)for i := 1 to 20 dobeginrnd := Random(30);dict.Remove(rnd);end;//loop elements - go through keyslog.Lines.Add('ELEMENTS:');for i in dict.Keys dolog.Lines.Add(Format('%d, %s', [i, dict.Items[i]]));//do we have a "special" key valueif dict.TryGetValue(80, c) thenlog.Lines.Add(Format('Found "special", value: %s', [c]))elselog.Lines.Add(Format('"Special" key not found', []));//sort by keys ascendinglog.Lines.Add('KEYS SORTED ASCENDING:');sortedDictKeys := TList.Create(dict.Keys);trysortedDictKeys.Sort; //default ascendingfor i in sortedDictKeys dolog.Lines.Add(Format('%d, %s', [i, dict.Items[i]]));finallysortedDictKeys.Free;end;//sort by keys descendinglog.Lines.Add('KEYS SORTED DESCENDING:');sortedDictKeys := TList.Create(dict.Keys);trysortedDictKeys.Sort(TComparer.Construct(function (const L, R: integer): integerbeginresult := R - L;end)) ;for i in sortedDictKeys dolog.Lines.Add(Format('%d, %s', [i, dict.Items[i]]));finallysortedDictKeys.Free;end;finallydict.Free;end;end;

Primero, declaramos nuestro diccionario especificando cuáles serán los tipos de TKey y TValue:

 

dict : TDictionary;

 

A continuación, el diccionario se rellena con el método Add. Como un diccionario no puede tener dos pares con el mismo valor de clave, puede utilizar el método ContainsKey para comprobar si hay algún par valor de clave en el diccionario.

 

Para eliminar un par del diccionario, utilice el método Eliminar. Este método no causará problemas si un par con una clave especificada no forma parte del diccionario.

 

Para pasar a través de todos los pares de teclas se puede hacer un para en bucle.

Utilice el método TryGetValue para comprobar si algún par clave-valor está incluido en el diccionario.

 

Clasificación del diccionario

Debido a que un diccionario es una tabla hash, no almacena los elementos en un orden de clasificación definido. Para iterar a través de las claves que se clasifican para satisfacer sus necesidades específicas, aproveche la TList, un tipo de colección genérica que admite la clasificación.

 

El código de arriba clasifica las claves ascendente y descendente y toma los valores como si estuvieran almacenados en el orden orden ordenado en el diccionario. La clasificación descendente de los valores clave de tipo entero utiliza TComparer y un método anónimo.

Cuando las claves y los valores son de tipo objeto

El ejemplo anterior es simple porque tanto la clave como el valor son tipos simples. Puede tener diccionarios complejos donde tanto la clave como el valor son tipos "complejos" como registros u objetos.

 

Aquí hay otro ejemplo:

 

typeTMyRecord = recordName, Surname : stringend;TMyObject = class(TObject)Year, Value : integer;end;procedure TForm2.logDblClick(Sender: TObject);vardict : TObjectDictionary<TMyRecord, TMyObject>;myR : TmyRecord;myO : TMyObject;begindict := TObjectDictionary<TMyRecord, TMyObject>.Create([doOwnsValues]);trymyR.Name := 'Zarko';myR.Surname := 'Gajic';myO := TMyObject.Create;myO.Year := 2012;myO.Value := 39;dict.Add(myR, myO);myR.Name := 'Zarko';myR.Surname := '?????';if NOT dict.ContainsKey(myR) then log.Lines.Add('not found');finallydict.Free;end;end;

 

Aquí se utiliza un registro personalizado para la clave y un objeto/clase personalizado para el valor.

Observe el uso de una clase especializada de TObjectDictionary aquí. TObjectDictionary puede manejar la vida útil de los objetos automáticamente.

 

El valor de la clave no puede ser nulo, mientras que el valor del valor puede ser nulo.

 

Cuando un TObjectDiccionario es instanciado, un parámetro Ownerships especifica si el diccionario posee las claves, los valores o ambos - y por lo tanto les ayuda a no tener fugas de memoria.

(0 votes)