I assume that you only want to store elements on the heap in order to access them by reference for manipulation. However, you don't need to do that when using Ada containers. All containers have some way of accessing the elements by reference readily available (via some Constant_Reference or Reference function that can typically be omitted because of the Variable_Indexing aspect defined on the container type; see, for example, section 6.3 in the Ada 2012 rationale, and/or the answer of @Timur Samkharadze).
If you want to store the key as part of the element, then I think it might be more appropriate to use a hashed set (see RM A.18.7, RM A.18.8 and on learn.adacore.com). An element in a hashed set can be accessed by reference via the function Reference_Preserving_Key (see also RM 96.10 (3)).
Below are two examples: the first example shows how to update an element in a Hashed_Map and the second example shows how to update an element in a Hashed_Set, both using a key:
main.adb (Hashed_Map)
with Ada.Text_IO; use Ada.Text_IO; with Ada.Containers; use Ada.Containers; with Ada.Containers.Hashed_Maps; procedure Main is type Thing_Key is new Integer; type Thing is record Key : Thing_Key; Data : Integer; end record; function Image (T : Thing) return String is ("Key = " & T.Key'Image & ", Value = " & T.Data'Image); function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K)); package Things is new Ada.Containers.Hashed_Maps (Key_Type => Thing_Key, Element_Type => Thing, Hash => Hash, Equivalent_Keys => "="); Map : Things.Map; begin -- Inserting 4 elements. Note that the key is now stored twice: once in -- the map's key index (its hash, to be more precise), and once in the item -- itself (unhashed). You must now ensure that the key value in the -- element does not accidentally get out-of-sync with the hashed key in the -- map's key index (e.g. when you update the stored element). Of course, -- you could also you just omit the key in the element itself if possible -- given your use-case. Map.Insert (Key => 1, New_Item => (Key => 1, Data => 10)); Map.Insert (Key => 2, New_Item => (Key => 2, Data => 20)); Map.Insert (Key => 3, New_Item => (Key => 3, Data => 30)); Map.Insert (Key => 4, New_Item => (Key => 4, Data => 40)); for T of Map loop Put_Line (Image (T)); end loop; New_Line; -- Update element with key 3. -- -- Note that the following expressions are all equivalent: -- -- Map.Reference (3).Element.all.Data := 300; -- Original expression -- Map.Reference (3).Element.Data := 300; -- Omit "all" due to implicit dereferencing of access types in Ada. -- Map.Reference (3).Data := 300; -- Omit "Element" due to the "Implicit_Dereferencing" aspect on the "Hashed_Maps.Reference_Type". -- Map (3).Data := 300; -- Omit "Reference" due to the "Variable_Indexing" aspect on the "Hashed_Maps.Map" type. -- Map (3).Data := 300; -- Example if you really need a pointer to element with key 3. declare type Thing_Access is not null access all Thing; type Thing_Constant_Access is not null access constant Thing; -- Element is mutable via P , i.e. P.Data := 301 (OK) -- Element is not mutable via CP, i.e. CP.Data := 302 (Error) P : Thing_Access := Map.Reference (3).Element; CP : Thing_Constant_Access := Map.Constant_Reference (3).Element; begin null; end; for T of Map loop Put_Line (Image (T)); end loop; New_Line; end Main;
main.adb (Hashed_Set)
with Ada.Text_IO; use Ada.Text_IO; with Ada.Containers; use Ada.Containers; with Ada.Containers.Hashed_Sets; procedure Main is type Thing_Key is new Integer; type Thing is record Key : Thing_Key; Data : Integer; end record; function Image (T : Thing) return String is ("Key = " & T.Key'Image & ", Value = " & T.Data'Image); function Key (T : Thing) return Thing_Key is (T.Key); function Hash (T : Thing) return Hash_Type is (Hash_Type (T.Key)); function Hash (K : Thing_Key) return Hash_Type is (Hash_Type (K)); package Things is new Ada.Containers.Hashed_Sets (Element_Type => Thing, Hash => Hash, Equivalent_Elements => "="); package Things_Keys is new Things.Generic_Keys (Key_Type => Thing_Key, Key => Key, Hash => Hash, Equivalent_Keys => "="); Set : Things.Set; begin -- Inserting 4 elements. Note that the key is stored only in the element. Set.Insert ((Key => 1, Data => 10)); Set.Insert ((Key => 2, Data => 20)); Set.Insert ((Key => 3, Data => 30)); Set.Insert ((Key => 4, Data => 40)); for T of Set loop Put_Line (Image (T)); end loop; New_Line; -- Update the element. See also RM 96.10 (3). Opposed to most other -- containers, you cannot omit "Reference_Preserving_Key" as the "Set" type -- does not have a "Variable_Indexing" aspect specifying "Reference_Preserving_Key". -- Hence, you need write it out explicitly. Things_Keys.Reference_Preserving_Key (Set, 3).Data := 300; -- Example if you really need a pointer to element with key 3. declare type Thing_Access is not null access all Thing; type Thing_Constant_Access is not null access constant Thing; -- Element is mutable via P , i.e. P.Data := 301 (OK) -- Element is not mutable via CP, i.e. CP.Data := 302 (Error) P : Thing_Access := Things_Keys.Reference_Preserving_Key (Set, 3).Element; CP : Thing_Constant_Access := Things_Keys.Constant_Reference (Set, 3).Element; begin null; end; for T of Set loop Put_Line (Image (T)); end loop; New_Line; end Main;
output (same for both)
Key = 1, Value = 10 Key = 2, Value = 20 Key = 3, Value = 30 Key = 4, Value = 40 Key = 1, Value = 10 Key = 2, Value = 20 Key = 3, Value = 300 Key = 4, Value = 40
Hashed_Setinstead of a 'Hashed_Map` given that I do not know the exact use-case, maybe the answer was not enough to-the-point, or maybe I really made a mistake in my example. Anyway, Timor's answer was already good and so I decided to remove it just to be sure. I'll put it back for you to have another look at it.