Maps/ADT: Difference between revisions
From charlesreid1
(Add Python mapping protocol section alongside the existing Java Map interface section (via update-page on MediaWiki MCP Server)) |
|||
| (2 intermediate revisions by the same user not shown) | |||
| Line 1: | Line 1: | ||
The '''Map Abstract Data Type''' (ADT) defines the interface contract that all [[Maps|map]] implementations must satisfy. It specifies ''what'' operations a map supports without prescribing ''how'' they are implemented — the hallmark of an ADT. A concrete map implementation may use a hash table, a balanced tree, a sorted array, or any other suitable data structure. | |||
A map stores key-value pairs. Each key maps to exactly one value (the map is a [[Wikipedia:Function (mathematics)|function]] from a domain of keys to a codomain of values). Keys must be unique; distinct keys may map to the same value. | |||
==Basic operations== | |||
== | These five operations constitute the essential interface of any map ADT: | ||
{| class="wikitable" | |||
|- | |||
! Operation !! Pseudocode signatures !! Description | |||
|- | |||
| '''get''' || <code>m[k]</code>, <code>m.get(k)</code> | |||
| Return the value associated with key <code>k</code>; raise a ''key error'' if <code>k</code> is absent. | |||
|- | |||
| '''set''' || <code>m[k] = v</code>, <code>m.put(k, v)</code> | |||
| Associate value <code>v</code> with key <code>k</code>, overwriting any previous mapping. | |||
|- | |||
| '''remove''' || <code>del m[k]</code>, <code>m.remove(k)</code> | |||
| Remove the entry for key <code>k</code>; raise a key error if absent. | |||
|- | |||
| '''size''' || <code>len(m)</code>, <code>m.size()</code> | |||
| Return the number of key-value pairs in the map. | |||
|- | |||
| '''iterate''' || <code>iter(m)</code> | |||
| Return an iterator over the keys of the map. | |||
|} | |||
==Convenience operations== | |||
These operations can each be expressed in terms of the basic five above, but are provided for programmer convenience. | |||
===Lookup=== | |||
* <code>m.contains(k)</code> / <code>k '''in''' m</code> — test whether key <code>k</code> exists in the map; returns boolean | |||
* <code>m.get(k, d)</code> — return <code>m[k]</code> if key <code>k</code> exists; otherwise return default value <code>d</code> | |||
* <code>m.setdefault(k, d)</code> — if key <code>k</code> exists return <code>m[k]</code>; otherwise set <code>m[k] = d</code> and return <code>d</code> | |||
===Modification=== | |||
* <code>m.pop(k, d)</code> — remove key <code>k</code> and return its associated value; return <code>d</code> if key is absent | |||
* <code>m.popitem()</code> — remove and return an arbitrary <code>(k, v)</code> pair; raise a key error if the map is empty | |||
* <code>m.clear()</code> — remove all key-value pairs from the map | |||
===Views=== | |||
* <code>m.keys()</code> — return a set-like view of all keys | |||
* <code>m.values()</code> — return a set-like view of all values | |||
* <code>m.items()</code> — return a set-like view of all <code>(k, v)</code> tuples | |||
Views are live: changes to the underlying map are reflected in the view, and vice versa. | |||
===Bulk operations=== | |||
* <code>m.update(m2)</code> — for every <code>(k, v)</code> pair in map <code>m2</code>, set <code>m[k] = v</code> | |||
* <code>m.equals(m2)</code> — return true if the two maps contain the same key-value pairs | |||
==Expected complexity== | |||
While the ADT itself is implementation-agnostic, useful map implementations achieve the following amortized bounds: | |||
{| class="wikitable" | |||
|- | |||
! Implementation !! <code>get</code> !! <code>put</code> !! <code>remove</code> !! <code>contains</code> | |||
|- | |||
| Hash table || O(1) || O(1) || O(1) || O(1) | |||
|- | |||
| Balanced BST || O(log n) || O(log n) || O(log n) || O(log n) | |||
|- | |||
| Unsorted array || O(n) || O(n) || O(n) || O(n) | |||
|- | |||
| Sorted array || O(log n) || O(n) || O(n) || O(log n) | |||
|} | |||
==Java Map interface== | |||
The Java Collections Framework specifies the map ADT through the <code>[http://docs.oracle.com/javase/8/docs/api/java/util/Map.html java.util.Map<K,V>]</code> interface. | |||
===Core methods=== | |||
{| class="wikitable" | |||
|- | |||
! Method !! Description | |||
|- | |||
| <code>size()</code> || Return the number of key-value mappings. | |||
|- | |||
| <code>isEmpty()</code> || Return <code>true</code> if the map contains no mappings. | |||
|- | |||
| <code>get(Object key)</code> || Return the value to which <code>key</code> is mapped, or <code>null</code> if absent. | |||
|- | |||
| <code>put(K key, V value)</code> || Associate <code>value</code> with <code>key</code>; return the previous value, or <code>null</code>. | |||
|- | |||
| <code>remove(Object key)</code> || Remove the mapping for <code>key</code>; return the previous value, or <code>null</code>. | |||
|- | |||
| <code>containsKey(Object key)</code> || Return <code>true</code> if the map contains <code>key</code>. | |||
|- | |||
| <code>containsValue(Object value)</code> || Return <code>true</code> if the map maps one or more keys to <code>value</code>. | |||
|- | |||
| <code>keySet()</code> || Return a <code>Set<K></code> view of the keys. | |||
|- | |||
| <code>values()</code> || Return a <code>Collection<V></code> view of the values. | |||
|- | |||
| <code>entrySet()</code> || Return a <code>Set<Map.Entry<K,V>></code> view of the entries. | |||
|} | |||
===Bulk and default methods=== | |||
{| class="wikitable" | |||
|- | |||
! Method !! Description | |||
|- | |||
| <code>putAll(Map m)</code> || Copy all mappings from <code>m</code> into this map. | |||
|- | |||
| <code>clear()</code> || Remove all mappings. | |||
|- | |||
| <code>getOrDefault(Object key, V def)</code> || Return value for <code>key</code>, or <code>def</code> if absent. (Java 8) | |||
|- | |||
| <code>putIfAbsent(K key, V value)</code> || Insert only if <code>key</code> is not already present. (Java 8) | |||
|- | |||
| <code>replace(K key, V old, V new)</code> || Replace value only if currently mapped to <code>old</code>. (Java 8) | |||
|- | |||
| <code>computeIfAbsent(K key, Function f)</code> || If <code>key</code> is absent, compute and insert a value via <code>f</code>. (Java 8) | |||
|- | |||
| <code>merge(K key, V val, BiFunction remap)</code> || Merge <code>val</code> with existing value using <code>remap</code>. (Java 8) | |||
|} | |||
Standard implementations: <code>HashMap</code> (hash table, O(1) expected), <code>TreeMap</code> (red-black tree, O(log n)), <code>LinkedHashMap</code> (hash table with insertion-order iteration). | |||
==Python mapping protocol== | |||
Python specifies the map ADT through two abstract base classes in <code>[https://docs.python.org/3/library/collections.abc.html collections.abc]</code>: the read-only <code>Mapping</code> and the mutable <code>MutableMapping</code> (which extends <code>Mapping</code>). The built-in <code>dict</code> type is the primary concrete implementation; user-defined map types can register as virtual subclasses or inherit directly from these ABCs. | |||
The ABC hierarchy relevant to maps: | |||
* <code>Container</code> → <code>Collection</code> → <code>Mapping</code> → <code>MutableMapping</code> | |||
* <code>Iterable</code> → <code>Collection</code> | |||
* <code>Sized</code> → <code>Collection</code> | |||
A minimal concrete <code>MutableMapping</code> must implement five abstract methods: <code>__getitem__</code>, <code>__setitem__</code>, <code>__delitem__</code>, <code>__iter__</code>, and <code>__len__</code>. All other mixin methods are derived from these five. | |||
===Core methods — <code>Mapping</code> (read-only)=== | |||
{| class="wikitable" | |||
|- | |||
! Method !! Description | |||
|- | |||
| <code>m[k]</code> (<code>__getitem__</code>) || Return the value for key <code>k</code>; raise <code>KeyError</code> if absent. ''Abstract.'' | |||
|- | |||
| <code>k '''in''' m</code> (<code>__contains__</code>) || Return <code>True</code> if key <code>k</code> is present. Default: iterate and compare. | |||
|- | |||
| <code>len(m)</code> (<code>__len__</code>) || Return the number of key-value pairs. ''Abstract.'' | |||
|- | |||
| <code>iter(m)</code> (<code>__iter__</code>) || Return an iterator over the keys. ''Abstract.'' | |||
|- | |||
| <code>m.keys()</code> || Return a <code>KeysView</code> (set-like) of all keys. | |||
|- | |||
| <code>m.values()</code> || Return a <code>ValuesView</code> of all values. | |||
|- | |||
| <code>m.items()</code> || Return an <code>ItemsView</code> (set-like) of <code>(k, v)</code> tuples. | |||
|- | |||
| <code>m.get(k, d=None)</code> || Return <code>m[k]</code> if key exists; otherwise return default <code>d</code>. | |||
|- | |||
| <code>m == m2</code> (<code>__eq__</code>) || Return <code>True</code> if both maps have the same key-value pairs. | |||
|} | |||
All views are live: they reflect changes to the underlying map. | |||
===Mutable methods — <code>MutableMapping</code>=== | |||
{| class="wikitable" | |||
|- | |||
! Method !! Description | |||
|- | |||
| <code>m[k] = v</code> (<code>__setitem__</code>) || Insert or overwrite the mapping for key <code>k</code>. ''Abstract.'' | |||
|- | |||
| <code>del m[k]</code> (<code>__delitem__</code>) || Remove the mapping for key <code>k</code>; raise <code>KeyError</code> if absent. ''Abstract.'' | |||
|- | |||
| <code>m.pop(k, d)</code> || Remove key <code>k</code> and return its value; return <code>d</code> (or raise <code>KeyError</code>) if absent. | |||
|- | |||
| <code>m.popitem()</code> || Remove and return an arbitrary <code>(k, v)</code> pair; raise <code>KeyError</code> if empty. | |||
|- | |||
| <code>m.setdefault(k, d=None)</code> || Return <code>m[k]</code> if present; otherwise set <code>m[k] = d</code> and return <code>d</code>. | |||
|- | |||
| <code>m.clear()</code> || Remove all key-value pairs. | |||
|- | |||
| <code>m.update(m2)</code> || For every <code>(k, v)</code> in <code>m2</code> (or from keyword arguments), set <code>m[k] = v</code>. | |||
|} | |||
===Dict-specific methods=== | |||
Beyond the <code>MutableMapping</code> contract, <code>dict</code> provides: | |||
{| class="wikitable" | |||
|- | |||
! Method !! Description | |||
|- | |||
| <code>dict.fromkeys(iterable, v=None)</code> || Class method: build a new dict with keys from an iterable, all mapped to value <code>v</code>. | |||
|- | |||
| <code>m.copy()</code> || Return a shallow copy of the dict. | |||
|- | |||
| <code>m {{!}} m2</code> (<code>__or__</code>) || Return a new dict merging <code>m</code> and <code>m2</code>. (Python 3.9+) | |||
|- | |||
| <code>m {{!}}= m2</code> (<code>__ior__</code>) || In-place merge: update <code>m</code> with <code>m2</code>. (Python 3.9+) | |||
|- | |||
| <code>reversed(m)</code> (<code>__reversed__</code>) || Return a reverse iterator over keys. (Python 3.8+) | |||
|} | |||
===Standard implementations=== | |||
{| class="wikitable" | |||
|- | |||
! Class !! Module !! Backing !! Notes | |||
|- | |||
| <code>dict</code> || built-in || Hash table, O(1) expected || The default mapping type. Insertion-ordered since Python 3.7. | |||
|- | |||
| <code>OrderedDict</code> || <code>collections</code> || Doubly-linked list + hash table || Remembers insertion order (present since Python 3.1; <code>dict</code> also preserves order since 3.7, but <code>OrderedDict</code> adds <code>move_to_end</code> and order-sensitive equality). | |||
|- | |||
| <code>defaultdict</code> || <code>collections</code> || Hash table || Missing keys are auto-created by a user-supplied factory function. | |||
|- | |||
| <code>Counter</code> || <code>collections</code> || Hash table || Multiset: keys map to integer counts. Supports <code>+</code>, <code>-</code>, <code>&</code>, <code>|</code> operators. | |||
|- | |||
| <code>ChainMap</code> || <code>collections</code> || Linked list of mappings || Chains multiple mappings together; lookups search each in order. | |||
|- | |||
| <code>MappingProxyType</code> || <code>types</code> || Wraps a dict || Read-only dynamic view onto another mapping. | |||
|} | |||
Python has no balanced-BST map in the standard library. Third-party packages such as [https://grantjenks.com/docs/sortedcontainers/ sortedcontainers] provide <code>SortedDict</code> with O(log n) operations. | |||
===Example: minimal custom <code>MutableMapping</code>=== | |||
<syntaxhighlight lang="python"> | |||
from collections.abc import MutableMapping | |||
class SingleEntryMap(MutableMapping): | |||
"""A map that holds at most one key-value pair.""" | |||
def __init__(self): | |||
self._key = None | |||
self._value = None | |||
def __getitem__(self, key): | |||
if self._key == key: | |||
return self._value | |||
raise KeyError(key) | |||
def __setitem__(self, key, value): | |||
self._key = key | |||
self._value = value | |||
def __delitem__(self, key): | |||
if self._key == key: | |||
self._key = None | |||
self._value = None | |||
else: | |||
raise KeyError(key) | |||
def __iter__(self): | |||
if self._key is not None: | |||
yield self._key | |||
def __len__(self): | |||
return 1 if self._key is not None else 0 | |||
def __repr__(self): | |||
return repr(dict(self)) if self._key is not None else '{}' | |||
</syntaxhighlight> | |||
Implementing the five abstract methods automatically provides <code>keys()</code>, <code>values()</code>, <code>items()</code>, <code>get()</code>, <code>pop()</code>, <code>popitem()</code>, <code>setdefault()</code>, <code>clear()</code>, <code>update()</code>, <code>__contains__</code>, and <code>__eq__</code> — all supplied by the <code>MutableMapping</code> mixin. | |||
==See also== | |||
* [[Maps]] — main Maps page with implementation notes | |||
* [[Maps/AbstractMap]] — abstract base class | |||
* [[Maps/UnsortedArrayMap]] — array-based concrete implementation | |||
* [[Maps/SortedArrayMap]] — sorted-array concrete implementation | |||
* [[Hash Maps]] — hash-table implementations (chaining and probing) | |||
* [[Maps/Operations and Performance]] — timing benchmarks | |||
{{MapsFlag}} | {{MapsFlag}} | ||
Latest revision as of 04:12, 8 June 2026
The Map Abstract Data Type (ADT) defines the interface contract that all map implementations must satisfy. It specifies what operations a map supports without prescribing how they are implemented — the hallmark of an ADT. A concrete map implementation may use a hash table, a balanced tree, a sorted array, or any other suitable data structure.
A map stores key-value pairs. Each key maps to exactly one value (the map is a function from a domain of keys to a codomain of values). Keys must be unique; distinct keys may map to the same value.
Basic operations
These five operations constitute the essential interface of any map ADT:
| Operation | Pseudocode signatures | Description |
|---|---|---|
| get | m[k], m.get(k)
|
Return the value associated with key k; raise a key error if k is absent.
|
| set | m[k] = v, m.put(k, v)
|
Associate value v with key k, overwriting any previous mapping.
|
| remove | del m[k], m.remove(k)
|
Remove the entry for key k; raise a key error if absent.
|
| size | len(m), m.size()
|
Return the number of key-value pairs in the map. |
| iterate | iter(m)
|
Return an iterator over the keys of the map. |
Convenience operations
These operations can each be expressed in terms of the basic five above, but are provided for programmer convenience.
Lookup
m.contains(k)/k in m— test whether keykexists in the map; returns booleanm.get(k, d)— returnm[k]if keykexists; otherwise return default valuedm.setdefault(k, d)— if keykexists returnm[k]; otherwise setm[k] = dand returnd
Modification
m.pop(k, d)— remove keykand return its associated value; returndif key is absentm.popitem()— remove and return an arbitrary(k, v)pair; raise a key error if the map is emptym.clear()— remove all key-value pairs from the map
Views
m.keys()— return a set-like view of all keysm.values()— return a set-like view of all valuesm.items()— return a set-like view of all(k, v)tuples
Views are live: changes to the underlying map are reflected in the view, and vice versa.
Bulk operations
m.update(m2)— for every(k, v)pair in mapm2, setm[k] = vm.equals(m2)— return true if the two maps contain the same key-value pairs
Expected complexity
While the ADT itself is implementation-agnostic, useful map implementations achieve the following amortized bounds:
| Implementation | get |
put |
remove |
contains
|
|---|---|---|---|---|
| Hash table | O(1) | O(1) | O(1) | O(1) |
| Balanced BST | O(log n) | O(log n) | O(log n) | O(log n) |
| Unsorted array | O(n) | O(n) | O(n) | O(n) |
| Sorted array | O(log n) | O(n) | O(n) | O(log n) |
Java Map interface
The Java Collections Framework specifies the map ADT through the java.util.Map<K,V> interface.
Core methods
| Method | Description |
|---|---|
size() |
Return the number of key-value mappings. |
isEmpty() |
Return true if the map contains no mappings.
|
get(Object key) |
Return the value to which key is mapped, or null if absent.
|
put(K key, V value) |
Associate value with key; return the previous value, or null.
|
remove(Object key) |
Remove the mapping for key; return the previous value, or null.
|
containsKey(Object key) |
Return true if the map contains key.
|
containsValue(Object value) |
Return true if the map maps one or more keys to value.
|
keySet() |
Return a Set<K> view of the keys.
|
values() |
Return a Collection<V> view of the values.
|
entrySet() |
Return a Set<Map.Entry<K,V>> view of the entries.
|
Bulk and default methods
| Method | Description |
|---|---|
putAll(Map m) |
Copy all mappings from m into this map.
|
clear() |
Remove all mappings. |
getOrDefault(Object key, V def) |
Return value for key, or def if absent. (Java 8)
|
putIfAbsent(K key, V value) |
Insert only if key is not already present. (Java 8)
|
replace(K key, V old, V new) |
Replace value only if currently mapped to old. (Java 8)
|
computeIfAbsent(K key, Function f) |
If key is absent, compute and insert a value via f. (Java 8)
|
merge(K key, V val, BiFunction remap) |
Merge val with existing value using remap. (Java 8)
|
Standard implementations: HashMap (hash table, O(1) expected), TreeMap (red-black tree, O(log n)), LinkedHashMap (hash table with insertion-order iteration).
Python mapping protocol
Python specifies the map ADT through two abstract base classes in collections.abc: the read-only Mapping and the mutable MutableMapping (which extends Mapping). The built-in dict type is the primary concrete implementation; user-defined map types can register as virtual subclasses or inherit directly from these ABCs.
The ABC hierarchy relevant to maps:
Container→Collection→Mapping→MutableMappingIterable→CollectionSized→Collection
A minimal concrete MutableMapping must implement five abstract methods: __getitem__, __setitem__, __delitem__, __iter__, and __len__. All other mixin methods are derived from these five.
Core methods — Mapping (read-only)
| Method | Description |
|---|---|
m[k] (__getitem__) |
Return the value for key k; raise KeyError if absent. Abstract.
|
k in m (__contains__) |
Return True if key k is present. Default: iterate and compare.
|
len(m) (__len__) |
Return the number of key-value pairs. Abstract. |
iter(m) (__iter__) |
Return an iterator over the keys. Abstract. |
m.keys() |
Return a KeysView (set-like) of all keys.
|
m.values() |
Return a ValuesView of all values.
|
m.items() |
Return an ItemsView (set-like) of (k, v) tuples.
|
m.get(k, d=None) |
Return m[k] if key exists; otherwise return default d.
|
m == m2 (__eq__) |
Return True if both maps have the same key-value pairs.
|
All views are live: they reflect changes to the underlying map.
Mutable methods — MutableMapping
| Method | Description |
|---|---|
m[k] = v (__setitem__) |
Insert or overwrite the mapping for key k. Abstract.
|
del m[k] (__delitem__) |
Remove the mapping for key k; raise KeyError if absent. Abstract.
|
m.pop(k, d) |
Remove key k and return its value; return d (or raise KeyError) if absent.
|
m.popitem() |
Remove and return an arbitrary (k, v) pair; raise KeyError if empty.
|
m.setdefault(k, d=None) |
Return m[k] if present; otherwise set m[k] = d and return d.
|
m.clear() |
Remove all key-value pairs. |
m.update(m2) |
For every (k, v) in m2 (or from keyword arguments), set m[k] = v.
|
Dict-specific methods
Beyond the MutableMapping contract, dict provides:
| Method | Description |
|---|---|
dict.fromkeys(iterable, v=None) |
Class method: build a new dict with keys from an iterable, all mapped to value v.
|
m.copy() |
Return a shallow copy of the dict. |
m2 (__or__) |
Return a new dict merging m and m2. (Python 3.9+)
|
= m2 (__ior__) |
In-place merge: update m with m2. (Python 3.9+)
|
reversed(m) (__reversed__) |
Return a reverse iterator over keys. (Python 3.8+) |
Standard implementations
| Class | Module | Backing | Notes |
|---|---|---|---|
dict |
built-in | Hash table, O(1) expected | The default mapping type. Insertion-ordered since Python 3.7. |
OrderedDict |
collections |
Doubly-linked list + hash table | Remembers insertion order (present since Python 3.1; dict also preserves order since 3.7, but OrderedDict adds move_to_end and order-sensitive equality).
|
defaultdict |
collections |
Hash table | Missing keys are auto-created by a user-supplied factory function. |
Counter |
collections |
Hash table | operators. |
ChainMap |
collections |
Linked list of mappings | Chains multiple mappings together; lookups search each in order. |
MappingProxyType |
types |
Wraps a dict | Read-only dynamic view onto another mapping. |
Python has no balanced-BST map in the standard library. Third-party packages such as sortedcontainers provide SortedDict with O(log n) operations.
Example: minimal custom MutableMapping
from collections.abc import MutableMapping
class SingleEntryMap(MutableMapping):
"""A map that holds at most one key-value pair."""
def __init__(self):
self._key = None
self._value = None
def __getitem__(self, key):
if self._key == key:
return self._value
raise KeyError(key)
def __setitem__(self, key, value):
self._key = key
self._value = value
def __delitem__(self, key):
if self._key == key:
self._key = None
self._value = None
else:
raise KeyError(key)
def __iter__(self):
if self._key is not None:
yield self._key
def __len__(self):
return 1 if self._key is not None else 0
def __repr__(self):
return repr(dict(self)) if self._key is not None else '{}'
Implementing the five abstract methods automatically provides keys(), values(), items(), get(), pop(), popitem(), setdefault(), clear(), update(), __contains__, and __eq__ — all supplied by the MutableMapping mixin.
See also
- Maps — main Maps page with implementation notes
- Maps/AbstractMap — abstract base class
- Maps/UnsortedArrayMap — array-based concrete implementation
- Maps/SortedArrayMap — sorted-array concrete implementation
- Hash Maps — hash-table implementations (chaining and probing)
- Maps/Operations and Performance — timing benchmarks
| Maps and Dictionaries Part of Computer Science Notes
Series on Data Structures
Maps/Dictionaries Maps · Maps/ADT · Maps in Java · Maps/OOP · Maps/Operations and Performance Map implementations: Maps/AbstractMap · Maps/UnsortedArrayMap · Maps/SortedArrayMap Dictionary implementations: Dictionaries/LinkedDict · Dictionaries/ArrayDict
Hashes Hash Maps/OOP · Hash Maps/Operations and Performance Hash Maps/Dynamic Resizing · Hash Maps/Collision Handling with Chaining Hash functions: Hash Functions · Hash Functions/Cyclic Permutation Hash map implementations: Hash Maps/AbstractHashMap · Hash Maps/ChainedHashMap
Skip Lists · Java/ConcurrentSkipList · Java implementations: SkipList
Sets Sets · Sets/ADT · Sets in Java · Sets/OOP · Multisets
|