• Aucun résultat trouvé

The Map Abstract Data Type

Dans le document This page intentionally left blank (Page 96-100)

Sets and Maps

3.2.1 The Map Abstract Data Type

The Map ADT provides a great example of an ADT that can be implemented using one of many different data structures. Our definition of the Map ADT, which is provided next, includes the minimum set of operations necessary for using and managing a map.

3.2 Maps 77

Define Map ADT

A map is a container for storing a collection of data records in which each record is associated with a unique key. The key components must be comparable.

Map(): Creates a new empty map.

length(): Returns the number of key/value pairs in the map.

contains( key ): Determines if the given key is in the map and returnsTrue if the key is found andFalse otherwise.

add( key, value ): Adds a new key/value pair to the map if the key is not already in the map or replaces the data associated with the key if the key is in the map. ReturnsTrue if this is a new key andFalse if the data associated with the existing key is replaced.

remove( key ): Removes the key/value pair for the given key if it is in the map and raises an exception otherwise.

valueOf( key ): Returns the data record associated with the given key. The key must exist in the map or an exception is raised.

iterator(): Creates and returns an iterator that can be used to iterate over the keys in the map.

3.2.2 List-Based Implementation

We indicated earlier that many different data structures can be used to implement a map. Since we are trying to replicate the functionality of the dictionary provided by Python, we don’t want to use that structure. That leaves the use of an array or list. As with the Set ADT, both the array and list structures can be used, but the list is a better choice since it does not have a fixed size like an array and it can expand automatically as needed.

In the implementation of the Bag and Set ADTs, we used a single list to store the individual elements. For the Map ADT, however, we must store both a key component and the corresponding value component for each entry in the map.

We cannot simply add the component pairs to the list without some means of maintaining their association.

One approach is to use two lists, one for the keys and one for the correspond-ing values. Accesscorrespond-ing and manipulatcorrespond-ing the components is very similar to that used with the Bag and Set ADTs. The difference, however, is that the associa-tion between the component pairs must always be maintained as new entries are added and existing ones removed. To accomplish this, each key/value must be stored in corresponding elements of the parallel lists and that association must be maintained.

78 CHAPTER 3 Sets and Maps

Instead of using two lists to store the key/value entries in the map, we can use a single list. The individual keys and corresponding values can both be saved in a single object, with that object then stored in the list. A sample instance illustrating the data organization required for this approach is shown in Figure 3.4.

10015

Figure 3.4:The Map ADT implemented using a single list.

The implementation of the Map ADT using a single list is provided in List-ing 3.2. As we indicated earlier in Chapter 1, we want to avoid the use of tuples when storing structured data since it’s better practice to use classes with named fields. The MapEntrystorage class, defined in lines 56–59, will be used to store the individual key/value pairs. Note this storage class is defined to be private since it’s only intended for use by theMapclass that provides the single list implementation of the Map ADT.

Listing 3.2 Thelinearmap.pymodule.

1 # Implementation of Map ADT using a single list.

2 class Map :

3 # Creates an empty map instance.

4 def __init__( self ):

5 self._entryList = list() 6

7 # Returns the number of entries in the map.

8 def __len__( self ):

9 return len( self._entryList ) 10

11 # Determines if the map contains the given key.

12 def __contains__( self, key ):

13 ndx = self._findPosition( key ) 14 return ndx is not None

15

16 # Adds a new entry to the map if the key does exist. Otherwise, the

3.2 Maps 79

17 # new value replaces the current value associated with the key.

18 def add( self, key, value ):

19 ndx = self._findPosition( key )

20 if ndx is not None : # if the key was found 21 self._entryList[ndx].value = value

22 return False

23 else : # otherwise add a new entry 24 entry = _MapEntry( key, value ) 25 self._entryList.append( entry ) 26 return True

27

28 # Returns the value associated with the key.

29 def valueOf( self, key ):

30 ndx = self._findPosition( key )

31 assert ndx is not None, "Invalid map key."

32 return self._entryList[ndx].value 33

34 # Removes the entry associated with the key.

35 def remove( self, key ):

36 ndx = self._findPosition( key )

37 assert ndx is not None, "Invalid map key."

38 self._entryList.pop( ndx ) 39

40 # Returns an iterator for traversing the keys in the map.

41 def __iter__( self ):

42 return _MapIterator( self._entryList ) 43

44 # Helper method used to find the index position of a category. If the 45 # key is not found, None is returned.

46 def _findPosition( self, key ):

47 # Iterate through each entry in the list.

48 for i in range( len(self) ) :

49 # Is the key stored in the ith entry?

50 if self._entryList[i].key == key :

51 return i

52 # When not found, return None.

53 return None 54

55 # Storage class for holding the key/value pairs.

56 class _MapEntry :

57 def __init__( self, key, value ):

58 self.key = key 59 self.value = value

Many of the methods require a search to determine if the map contains a given key. In this implementation, the standardinoperator cannot be used since the list contains MapEntryobjects and not simply key entries. Instead, we have to search the list ourselves and examine thekeyfield of each MapEntryobject. Likewise, we routinely have to locate within the list the position containing a specific key/value entry. Since these operations will be needed in several methods, we can create a helper method that combines the two searches and use it where needed.

The findPosition() helper method searches the list for the given key. If the key is found, the index of its location is returned; otherwise, the function

80 CHAPTER 3 Sets and Maps

returns None to indicate the key is not contained in the map. When used by the other methods, the value returned can be evaluated to determine both the existence of the key and the location of the corresponding entry if the key is in the map. By combining the two searches into a single operation, we eliminate the need to first determine if the map contains the key and then searching again for its location. Given the helper method, the implementation of the various methods is straightforward. Implementation of the iterator method is left as an exercise.

Dans le document This page intentionally left blank (Page 96-100)