1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.collection.Map;
13 
14 import hunt.collection.Collection;
15 import hunt.collection.Set;
16 import hunt.util.Common;
17 import hunt.Functions;
18 import hunt.Object;
19 
20 import std.range.interfaces : InputRange;
21 
22 /**
23 */
24 interface Map(K,V) : Iterable!(K,V), IObject {
25     // Query Operations
26 
27     /**
28      * Returns the number of key-value mappings in this map.  If the
29      * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
30      * <tt>Integer.MAX_VALUE</tt>.
31      *
32      * @return the number of key-value mappings in this map
33      */
34     int size();
35 
36     /**
37      * Returns <tt>true</tt> if this map contains no key-value mappings.
38      *
39      * @return <tt>true</tt> if this map contains no key-value mappings
40      */
41     bool isEmpty();
42 
43     /**
44      * Returns <tt>true</tt> if this map contains a mapping for the specified
45      * key.  More formally, returns <tt>true</tt> if and only if
46      * this map contains a mapping for a key <tt>k</tt> such that
47      * <tt>(key==null ? k==null : key.equals(k))</tt>.  (There can be
48      * at most one such mapping.)
49      *
50      * @param key key whose presence in this map is to be tested
51      * @return <tt>true</tt> if this map contains a mapping for the specified
52      *         key
53      * @throws ClassCastException if the key is of an inappropriate type for
54      *         this map
55      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
56      * @throws NullPointerException if the specified key is null and this map
57      *         does not permit null keys
58      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
59      */
60     bool containsKey(K key);
61 
62     /**
63      * Returns <tt>true</tt> if this map maps one or more keys to the
64      * specified value.  More formally, returns <tt>true</tt> if and only if
65      * this map contains at least one mapping to a value <tt>v</tt> such that
66      * <tt>(value==null ? v==null : value.equals(v))</tt>.  This operation
67      * will probably require time linear in the map size for most
68      * implementations of the <tt>Map</tt> interface.
69      *
70      * @param value value whose presence in this map is to be tested
71      * @return <tt>true</tt> if this map maps one or more keys to the
72      *         specified value
73      * @throws ClassCastException if the value is of an inappropriate type for
74      *         this map
75      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
76      * @throws NullPointerException if the specified value is null and this
77      *         map does not permit null values
78      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
79      */
80     bool containsValue(V value);
81 
82     /**
83      * Returns the value to which the specified key is mapped,
84      * or {@code null} if this map contains no mapping for the key.
85      *
86      * <p>More formally, if this map contains a mapping from a key
87      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
88      * key.equals(k))}, then this method returns {@code v}; otherwise
89      * it returns {@code null}.  (There can be at most one such mapping.)
90      *
91      * <p>If this map permits null values, then a return value of
92      * {@code null} does not <i>necessarily</i> indicate that the map
93      * contains no mapping for the key; it's also possible that the map
94      * explicitly maps the key to {@code null}.  The {@link #containsKey
95      * containsKey} operation may be used to distinguish these two cases.
96      *
97      * @param key the key whose associated value is to be returned
98      * @return the value to which the specified key is mapped, or
99      *         {@code null} if this map contains no mapping for the key
100      * @throws ClassCastException if the key is of an inappropriate type for
101      *         this map
102      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
103      * @throws NullPointerException if the specified key is null and this map
104      *         does not permit null keys
105      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
106      */
107     V get(K key);
108 
109     V opIndex(K key);
110 
111     // Modification Operations
112 
113     /**
114      * Associates the specified value with the specified key in this map
115      * (optional operation).  If the map previously contained a mapping for
116      * the key, the old value is replaced by the specified value.  (A map
117      * <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
118      * if {@link #containsKey(Object) m.containsKey(k)} would return
119      * <tt>true</tt>.)
120      *
121      * @param key key with which the specified value is to be associated
122      * @param value value to be associated with the specified key
123      * @return the previous value associated with <tt>key</tt>, or
124      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
125      *         (A <tt>null</tt> return can also indicate that the map
126      *         previously associated <tt>null</tt> with <tt>key</tt>,
127      *         if the implementation supports <tt>null</tt> values.)
128      * @throws UnsupportedOperationException if the <tt>put</tt> operation
129      *         is not supported by this map
130      * @throws ClassCastException if the class of the specified key or value
131      *         prevents it from being stored in this map
132      * @throws NullPointerException if the specified key or value is null
133      *         and this map does not permit null keys or values
134      * @throws IllegalArgumentException if some property of the specified key
135      *         or value prevents it from being stored in this map
136      */
137     V put(K key, V value);
138 
139     /**
140      * Removes the mapping for a key from this map if it is present
141      * (optional operation).   More formally, if this map contains a mapping
142      * from key <tt>k</tt> to value <tt>v</tt> such that
143      * <code>(key==null ?  k==null : key.equals(k))</code>, that mapping
144      * is removed.  (The map can contain at most one such mapping.)
145      *
146      * <p>Returns the value to which this map previously associated the key,
147      * or <tt>null</tt> if the map contained no mapping for the key.
148      *
149      * <p>If this map permits null values, then a return value of
150      * <tt>null</tt> does not <i>necessarily</i> indicate that the map
151      * contained no mapping for the key; it's also possible that the map
152      * explicitly mapped the key to <tt>null</tt>.
153      *
154      * <p>The map will not contain a mapping for the specified key once the
155      * call returns.
156      *
157      * @param key key whose mapping is to be removed from the map
158      * @return the previous value associated with <tt>key</tt>, or
159      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
160      * @throws UnsupportedOperationException if the <tt>remove</tt> operation
161      *         is not supported by this map
162      * @throws ClassCastException if the key is of an inappropriate type for
163      *         this map
164      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
165      * @throws NullPointerException if the specified key is null and this
166      *         map does not permit null keys
167      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
168      */
169     V remove(K key);
170 
171 
172     // Bulk Operations
173 
174     /**
175      * Copies all of the mappings from the specified map to this map
176      * (optional operation).  The effect of this call is equivalent to that
177      * of calling {@link #put(Object,Object) put(k, v)} on this map once
178      * for each mapping from key <tt>k</tt> to value <tt>v</tt> in the
179      * specified map.  The behavior of this operation is undefined if the
180      * specified map is modified while the operation is in progress.
181      *
182      * @param m mappings to be stored in this map
183      * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
184      *         is not supported by this map
185      * @throws ClassCastException if the class of a key or value in the
186      *         specified map prevents it from being stored in this map
187      * @throws NullPointerException if the specified map is null, or if
188      *         this map does not permit null keys or values, and the
189      *         specified map contains null keys or values
190      * @throws IllegalArgumentException if some property of a key or value in
191      *         the specified map prevents it from being stored in this map
192      */
193     void putAll(Map!(K, V) m);
194 
195     /**
196      * Removes all of the mappings from this map (optional operation).
197      * The map will be empty after this call returns.
198      *
199      * @throws UnsupportedOperationException if the <tt>clear</tt> operation
200      *         is not supported by this map
201      */
202     void clear();
203 
204     // Views
205 
206     /**
207      * Returns a {@link Collection} view of the values contained in this map.
208      * The collection is backed by the map, so changes to the map are
209      * reflected in the collection, and vice-versa.  If the map is
210      * modified while an iteration over the collection is in progress
211      * (except through the iterator's own <tt>remove</tt> operation),
212      * the results of the iteration are undefined.  The collection
213      * supports element removal, which removes the corresponding
214      * mapping from the map, via the <tt>Iterator.remove</tt>,
215      * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
216      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
217      * support the <tt>add</tt> or <tt>addAll</tt> operations.
218      *
219      * @return a collection view of the values contained in this map
220      */
221     // Collection!V values();
222     V[] values();
223 
224     string toString();
225 
226     
227     // Comparison and hashing
228 
229     /**
230      * Compares the specified object with this map for equality.  Returns
231      * <tt>true</tt> if the given object is also a map and the two maps
232      * represent the same mappings.  More formally, two maps <tt>m1</tt> and
233      * <tt>m2</tt> represent the same mappings if
234      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
235      * <tt>equals</tt> method works properly across different implementations
236      * of the <tt>Map</tt> interface.
237      *
238      * @param o object to be compared for equality with this map
239      * @return <tt>true</tt> if the specified object is equal to this map
240      */
241     // bool opEquals(Object o);
242 
243     /**
244      * Returns the hash code value for this map.  The hash code of a map is
245      * defined to be the sum of the hash codes of each entry in the map's
246      * <tt>entrySet()</tt> view.  This ensures that <tt>m1.equals(m2)</tt>
247      * implies that <tt>m1.toHash()==m2.toHash()</tt> for any two maps
248      * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
249      * {@link Object#toHash}.
250      *
251      * @return the hash code value for this map
252      * @see MapEntry#toHash()
253      * @see Object#equals(Object)
254      * @see #equals(Object)
255      */
256     size_t toHash() @trusted nothrow;
257 
258     // Defaultable methods
259 
260     /**
261      * Returns the value to which the specified key is mapped, or
262      * {@code defaultValue} if this map contains no mapping for the key.
263      *
264      * @implSpec
265      * The final implementation makes no guarantees about synchronization
266      * or atomicity properties of this method. Any implementation providing
267      * atomicity guarantees must override this method and document its
268      * concurrency properties.
269      *
270      * @param key the key whose associated value is to be returned
271      * @param defaultValue the final mapping of the key
272      * @return the value to which the specified key is mapped, or
273      * {@code defaultValue} if this map contains no mapping for the key
274      * @throws ClassCastException if the key is of an inappropriate type for
275      * this map
276      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
277      * @throws NullPointerException if the specified key is null and this map
278      * does not permit null keys
279      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
280      * @since 1.8
281      */
282     // final V getOrDefault(K key, V defaultValue) {
283     //     V v;
284     //     static if(is(V == class))
285     //     {            
286     //         return (((v = get(key)) !is null) || containsKey(key))
287     //             ? v
288     //             : defaultValue;
289     //     }
290     //     else
291     //     {
292     //         return containsKey(key) ? v : defaultValue;
293     //     }
294     // }
295 
296     /**
297      * Performs the given action for each entry in this map until all entries
298      * have been processed or the action throws an exception.   Unless
299      * otherwise specified by the implementing class, actions are performed in
300      * the order of entry set iteration (if an iteration order is specified.)
301      * Exceptions thrown by the action are relayed to the caller.
302      *
303      * @implSpec
304      * The final implementation is equivalent to, for this {@code map}:
305      * <pre> {@code
306      * for (MapEntry<K, V> entry : map.entrySet())
307      *     action.accept(entry.getKey(), entry.getValue());
308      * }</pre>
309      *
310      * The final implementation makes no guarantees about synchronization
311      * or atomicity properties of this method. Any implementation providing
312      * atomicity guarantees must override this method and document its
313      * concurrency properties.
314      *
315      * @param action The action to be performed for each entry
316      * @throws NullPointerException if the specified action is null
317      * @throws ConcurrentModificationException if an entry is found to be
318      * removed during iteration
319      * @since 1.8
320      */
321     int opApply(scope int delegate(ref K, ref V) dg);
322 
323     /// ditto
324     int opApply(scope int delegate(MapEntry!(K, V) entry) dg);
325 
326     InputRange!K byKey();
327 
328     InputRange!V byValue();
329 
330     /**
331      * Replaces each entry's value with the result of invoking the given
332      * function on that entry until all entries have been processed or the
333      * function throws an exception.  Exceptions thrown by the function are
334      * relayed to the caller.
335      *
336      * @implSpec
337      * <p>The final implementation is equivalent to, for this {@code map}:
338      * <pre> {@code
339      * for (MapEntry<K, V> entry : map.entrySet())
340      *     entry.setValue(function.apply(entry.getKey(), entry.getValue()));
341      * }</pre>
342      *
343      * <p>The final implementation makes no guarantees about synchronization
344      * or atomicity properties of this method. Any implementation providing
345      * atomicity guarantees must override this method and document its
346      * concurrency properties.
347      *
348      * @param function the function to apply to each entry
349      * @throws UnsupportedOperationException if the {@code set} operation
350      * is not supported by this map's entry set iterator.
351      * @throws ClassCastException if the class of a replacement value
352      * prevents it from being stored in this map
353      * @throws NullPointerException if the specified function is null, or the
354      * specified replacement value is null, and this map does not permit null
355      * values
356      * @throws ClassCastException if a replacement value is of an inappropriate
357      *         type for this map
358      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
359      * @throws NullPointerException if function or a replacement value is null,
360      *         and this map does not permit null keys or values
361      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
362      * @throws IllegalArgumentException if some property of a replacement value
363      *         prevents it from being stored in this map
364      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
365      * @throws ConcurrentModificationException if an entry is found to be
366      * removed during iteration
367      * @since 1.8
368      */
369     // final void replaceAll(BiFunction<K, V, V> function) {
370     //     Objects.requireNonNull(function);
371     //     for (MapEntry<K, V> entry : entrySet()) {
372     //         K k;
373     //         V v;
374     //         try {
375     //             k = entry.getKey();
376     //             v = entry.getValue();
377     //         } catch(IllegalStateException ise) {
378     //             // this usually means the entry is no longer in the map.
379     //             throw new ConcurrentModificationException(ise);
380     //         }
381 
382     //         // ise thrown from function is not a cme.
383     //         v = function.apply(k, v);
384 
385     //         try {
386     //             entry.setValue(v);
387     //         } catch(IllegalStateException ise) {
388     //             // this usually means the entry is no longer in the map.
389     //             throw new ConcurrentModificationException(ise);
390     //         }
391     //     }
392     // }
393 
394     /**
395      * If the specified key is not already associated with a value (or is mapped
396      * to {@code null}) associates it with the given value and returns
397      * {@code null}, else returns the current value.
398      *
399      * @implSpec
400      * The final implementation is equivalent to, for this {@code
401      * map}:
402      *
403      * <pre> {@code
404      * V v = map.get(key);
405      * if (v is null)
406      *     v = map.put(key, value);
407      *
408      * return v;
409      * }</pre>
410      *
411      * <p>The final implementation makes no guarantees about synchronization
412      * or atomicity properties of this method. Any implementation providing
413      * atomicity guarantees must override this method and document its
414      * concurrency properties.
415      *
416      * @param key key with which the specified value is to be associated
417      * @param value value to be associated with the specified key
418      * @return the previous value associated with the specified key, or
419      *         {@code null} if there was no mapping for the key.
420      *         (A {@code null} return can also indicate that the map
421      *         previously associated {@code null} with the key,
422      *         if the implementation supports null values.)
423      * @throws UnsupportedOperationException if the {@code put} operation
424      *         is not supported by this map
425      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
426      * @throws ClassCastException if the key or value is of an inappropriate
427      *         type for this map
428      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
429      * @throws NullPointerException if the specified key or value is null,
430      *         and this map does not permit null keys or values
431      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
432      * @throws IllegalArgumentException if some property of the specified key
433      *         or value prevents it from being stored in this map
434      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
435      * @since 1.8
436      */
437     V putIfAbsent(K key, V value);
438 
439     /**
440      * Removes the entry for the specified key only if it is currently
441      * mapped to the specified value.
442      *
443      * @implSpec
444      * The final implementation is equivalent to, for this {@code map}:
445      *
446      * <pre> {@code
447      * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
448      *     map.remove(key);
449      *     return true;
450      * } else
451      *     return false;
452      * }</pre>
453      *
454      * <p>The final implementation makes no guarantees about synchronization
455      * or atomicity properties of this method. Any implementation providing
456      * atomicity guarantees must override this method and document its
457      * concurrency properties.
458      *
459      * @param key key with which the specified value is associated
460      * @param value value expected to be associated with the specified key
461      * @return {@code true} if the value was removed
462      * @throws UnsupportedOperationException if the {@code remove} operation
463      *         is not supported by this map
464      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
465      * @throws ClassCastException if the key or value is of an inappropriate
466      *         type for this map
467      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
468      * @throws NullPointerException if the specified key or value is null,
469      *         and this map does not permit null keys or values
470      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
471      * @since 1.8
472      */
473     bool remove(K key, V value);
474 
475 
476     /**
477      * Replaces the entry for the specified key only if currently
478      * mapped to the specified value.
479      *
480      * @implSpec
481      * The final implementation is equivalent to, for this {@code map}:
482      *
483      * <pre> {@code
484      * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
485      *     map.put(key, newValue);
486      *     return true;
487      * } else
488      *     return false;
489      * }</pre>
490      *
491      * The final implementation does not throw NullPointerException
492      * for maps that do not support null values if oldValue is null unless
493      * newValue is also null.
494      *
495      * <p>The final implementation makes no guarantees about synchronization
496      * or atomicity properties of this method. Any implementation providing
497      * atomicity guarantees must override this method and document its
498      * concurrency properties.
499      *
500      * @param key key with which the specified value is associated
501      * @param oldValue value expected to be associated with the specified key
502      * @param newValue value to be associated with the specified key
503      * @return {@code true} if the value was replaced
504      * @throws UnsupportedOperationException if the {@code put} operation
505      *         is not supported by this map
506      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
507      * @throws ClassCastException if the class of a specified key or value
508      *         prevents it from being stored in this map
509      * @throws NullPointerException if a specified key or newValue is null,
510      *         and this map does not permit null keys or values
511      * @throws NullPointerException if oldValue is null and this map does not
512      *         permit null values
513      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
514      * @throws IllegalArgumentException if some property of a specified key
515      *         or value prevents it from being stored in this map
516      * @since 1.8
517      */
518     bool replace(K key, V oldValue, V newValue);
519 
520     /**
521      * Replaces the entry for the specified key only if it is
522      * currently mapped to some value.
523      *
524      * @implSpec
525      * The final implementation is equivalent to, for this {@code map}:
526      *
527      * <pre> {@code
528      * if (map.containsKey(key)) {
529      *     return map.put(key, value);
530      * } else
531      *     return null;
532      * }</pre>
533      *
534      * <p>The final implementation makes no guarantees about synchronization
535      * or atomicity properties of this method. Any implementation providing
536      * atomicity guarantees must override this method and document its
537      * concurrency properties.
538       *
539      * @param key key with which the specified value is associated
540      * @param value value to be associated with the specified key
541      * @return the previous value associated with the specified key, or
542      *         {@code null} if there was no mapping for the key.
543      *         (A {@code null} return can also indicate that the map
544      *         previously associated {@code null} with the key,
545      *         if the implementation supports null values.)
546      * @throws UnsupportedOperationException if the {@code put} operation
547      *         is not supported by this map
548      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
549      * @throws ClassCastException if the class of the specified key or value
550      *         prevents it from being stored in this map
551      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
552      * @throws NullPointerException if the specified key or value is null,
553      *         and this map does not permit null keys or values
554      * @throws IllegalArgumentException if some property of the specified key
555      *         or value prevents it from being stored in this map
556      * @since 1.8
557      */
558     V replace(K key, V value);
559 
560     /**
561      * If the specified key is not already associated with a value (or is mapped
562      * to {@code null}), attempts to compute its value using the given mapping
563      * function and enters it into this map unless {@code null}.
564      *
565      * <p>If the function returns {@code null} no mapping is recorded. If
566      * the function itself throws an (unchecked) exception, the
567      * exception is rethrown, and no mapping is recorded.  The most
568      * common usage is to construct a new object serving as an initial
569      * mapped value or memoized result, as in:
570      *
571      * <pre> {@code
572      * map.computeIfAbsent(key, k -> new Value(f(k)));
573      * }</pre>
574      *
575      * <p>Or to implement a multi-value map, {@code Map<K,Collection!V>},
576      * supporting multiple values per key:
577      *
578      * <pre> {@code
579      * map.computeIfAbsent(key, k -> new HashSet!V()).add(v);
580      * }</pre>
581      *
582      *
583      * @implSpec
584      * The final implementation is equivalent to the following steps for this
585      * {@code map}, then returning the current value or {@code null} if now
586      * absent:
587      *
588      * <pre> {@code
589      * if (map.get(key) is null) {
590      *     V newValue = mappingFunction.apply(key);
591      *     if (newValue !is null)
592      *         map.put(key, newValue);
593      * }
594      * }</pre>
595      *
596      * <p>The final implementation makes no guarantees about synchronization
597      * or atomicity properties of this method. Any implementation providing
598      * atomicity guarantees must override this method and document its
599      * concurrency properties. In particular, all implementations of
600      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
601      * whether the function is applied once atomically only if the value is not
602      * present.
603      *
604      * @param key key with which the specified value is to be associated
605      * @param mappingFunction the function to compute a value
606      * @return the current (existing or computed) value associated with
607      *         the specified key, or null if the computed value is null
608      * @throws NullPointerException if the specified key is null and
609      *         this map does not support null keys, or the mappingFunction
610      *         is null
611      * @throws UnsupportedOperationException if the {@code put} operation
612      *         is not supported by this map
613      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
614      * @throws ClassCastException if the class of the specified key or value
615      *         prevents it from being stored in this map
616      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
617      * @since 1.8
618      */
619 
620     final V computeIfAbsent(K key, Function!(K, V) mappingFunction) {
621         assert(mappingFunction !is null);
622         
623         if (containsKey(key)) {
624             return get(key);
625         }
626         else {
627             V newValue = mappingFunction(key);
628             static if(is(V == class))
629             {
630                 if (newValue !is null) {
631                     put(key, newValue);
632                     return newValue;
633                 }
634                 else
635                     return V.init;
636             }
637             else
638             {
639                 put(key, newValue);
640                 return newValue;
641             }
642         }
643     }
644 
645 
646     /**
647      * If the value for the specified key is present and non-null, attempts to
648      * compute a new mapping given the key and its current mapped value.
649      *
650      * <p>If the function returns {@code null}, the mapping is removed.  If the
651      * function itself throws an (unchecked) exception, the exception is
652      * rethrown, and the current mapping is left unchanged.
653     *
654      * @implSpec
655      * The final implementation is equivalent to performing the following
656      * steps for this {@code map}, then returning the current value or
657      * {@code null} if now absent:
658      *
659      * <pre> {@code
660      * if (map.get(key) !is null) {
661      *     V oldValue = map.get(key);
662      *     V newValue = remappingFunction.apply(key, oldValue);
663      *     if (newValue !is null)
664      *         map.put(key, newValue);
665      *     else
666      *         map.remove(key);
667      * }
668      * }</pre>
669      *
670      * <p>The final implementation makes no guarantees about synchronization
671      * or atomicity properties of this method. Any implementation providing
672      * atomicity guarantees must override this method and document its
673      * concurrency properties. In particular, all implementations of
674      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
675      * whether the function is applied once atomically only if the value is not
676      * present.
677      *
678      * @param key key with which the specified value is to be associated
679      * @param remappingFunction the function to compute a value
680      * @return the new value associated with the specified key, or null if none
681      * @throws NullPointerException if the specified key is null and
682      *         this map does not support null keys, or the
683      *         remappingFunction is null
684      * @throws UnsupportedOperationException if the {@code put} operation
685      *         is not supported by this map
686      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
687      * @throws ClassCastException if the class of the specified key or value
688      *         prevents it from being stored in this map
689      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
690      * @since 1.8
691      */
692     // final V computeIfPresent(K key,
693     //         BiFunction<K, V, V> remappingFunction) {
694     //     Objects.requireNonNull(remappingFunction);
695     //     V oldValue;
696     //     if ((oldValue = get(key)) !is null) {
697     //         V newValue = remappingFunction.apply(key, oldValue);
698     //         if (newValue !is null) {
699     //             put(key, newValue);
700     //             return newValue;
701     //         } else {
702     //             remove(key);
703     //             return null;
704     //         }
705     //     } else {
706     //         return null;
707     //     }
708     // }
709 
710     /**
711      * Attempts to compute a mapping for the specified key and its current
712      * mapped value (or {@code null} if there is no current mapping). For
713      * example, to either create or append a {@code string} msg to a value
714      * mapping:
715      *
716      * <pre> {@code
717      * map.compute(key, (k, v) -> (v is null) ? msg : v.concat(msg))}</pre>
718      * (Method {@link #merge merge()} is often simpler to use for such purposes.)
719      *
720      * <p>If the function returns {@code null}, the mapping is removed (or
721      * remains absent if initially absent).  If the function itself throws an
722      * (unchecked) exception, the exception is rethrown, and the current mapping
723      * is left unchanged.
724      *
725      * @implSpec
726      * The final implementation is equivalent to performing the following
727      * steps for this {@code map}, then returning the current value or
728      * {@code null} if absent:
729      *
730      * <pre> {@code
731      * V oldValue = map.get(key);
732      * V newValue = remappingFunction.apply(key, oldValue);
733      * if (oldValue !is null ) {
734      *    if (newValue !is null)
735      *       map.put(key, newValue);
736      *    else
737      *       map.remove(key);
738      * } else {
739      *    if (newValue !is null)
740      *       map.put(key, newValue);
741      *    else
742      *       return null;
743      * }
744      * }</pre>
745      *
746      * <p>The final implementation makes no guarantees about synchronization
747      * or atomicity properties of this method. Any implementation providing
748      * atomicity guarantees must override this method and document its
749      * concurrency properties. In particular, all implementations of
750      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
751      * whether the function is applied once atomically only if the value is not
752      * present.
753      *
754      * @param key key with which the specified value is to be associated
755      * @param remappingFunction the function to compute a value
756      * @return the new value associated with the specified key, or null if none
757      * @throws NullPointerException if the specified key is null and
758      *         this map does not support null keys, or the
759      *         remappingFunction is null
760      * @throws UnsupportedOperationException if the {@code put} operation
761      *         is not supported by this map
762      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
763      * @throws ClassCastException if the class of the specified key or value
764      *         prevents it from being stored in this map
765      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
766      * @since 1.8
767      */
768     // final V compute(K key,
769     //         BiFunction<K, V, V> remappingFunction) {
770     //     Objects.requireNonNull(remappingFunction);
771     //     V oldValue = get(key);
772 
773     //     V newValue = remappingFunction.apply(key, oldValue);
774     //     if (newValue is null) {
775     //         // delete mapping
776     //         if (oldValue !is null || containsKey(key)) {
777     //             // something to remove
778     //             remove(key);
779     //             return null;
780     //         } else {
781     //             // nothing to do. Leave things as they were.
782     //             return null;
783     //         }
784     //     } else {
785     //         // add or replace old mapping
786     //         put(key, newValue);
787     //         return newValue;
788     //     }
789     // }
790 
791     /**
792      * If the specified key is not already associated with a value or is
793      * associated with null, associates it with the given non-null value.
794      * Otherwise, replaces the associated value with the results of the given
795      * remapping function, or removes if the result is {@code null}. This
796      * method may be of use when combining multiple mapped values for a key.
797      * For example, to either create or append a {@code string msg} to a
798      * value mapping:
799      *
800      * <pre> {@code
801      * map.merge(key, msg, string::concat)
802      * }</pre>
803      *
804      * <p>If the function returns {@code null} the mapping is removed.  If the
805      * function itself throws an (unchecked) exception, the exception is
806      * rethrown, and the current mapping is left unchanged.
807      *
808      * @implSpec
809      * The final implementation is equivalent to performing the following
810      * steps for this {@code map}, then returning the current value or
811      * {@code null} if absent:
812      *
813      * <pre> {@code
814      * V oldValue = map.get(key);
815      * V newValue = (oldValue is null) ? value :
816      *              remappingFunction.apply(oldValue, value);
817      * if (newValue is null)
818      *     map.remove(key);
819      * else
820      *     map.put(key, newValue);
821      * }</pre>
822      *
823      * <p>The final implementation makes no guarantees about synchronization
824      * or atomicity properties of this method. Any implementation providing
825      * atomicity guarantees must override this method and document its
826      * concurrency properties. In particular, all implementations of
827      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
828      * whether the function is applied once atomically only if the value is not
829      * present.
830      *
831      * @param key key with which the resulting value is to be associated
832      * @param value the non-null value to be merged with the existing value
833      *        associated with the key or, if no existing value or a null value
834      *        is associated with the key, to be associated with the key
835      * @param remappingFunction the function to recompute a value if present
836      * @return the new value associated with the specified key, or null if no
837      *         value is associated with the key
838      * @throws UnsupportedOperationException if the {@code put} operation
839      *         is not supported by this map
840      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
841      * @throws ClassCastException if the class of the specified key or value
842      *         prevents it from being stored in this map
843      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
844      * @throws NullPointerException if the specified key is null and this map
845      *         does not support null keys or the value or remappingFunction is
846      *         null
847      * @since 1.8
848      */
849     // final V merge(K key, V value,
850     //         BiFunction<V, V, V> remappingFunction) {
851     //     Objects.requireNonNull(remappingFunction);
852     //     Objects.requireNonNull(value);
853     //     V oldValue = get(key);
854     //     V newValue = (oldValue is null) ? value :
855     //                remappingFunction.apply(oldValue, value);
856     //     if(newValue is null) {
857     //         remove(key);
858     //     } else {
859     //         put(key, newValue);
860     //     }
861     //     return newValue;
862     // }
863 }
864 
865 
866 /* A map entry (key-value pair).  The <tt>Map.entrySet</tt> method returns
867  * a collection-view of the map, whose elements are of this class.  The
868  * <i>only</i> way to obtain a reference to a map entry is from the
869  * iterator of this collection-view.  These <tt>MapEntry</tt> objects are
870  * valid <i>only</i> for the duration of the iteration; more formally,
871  * the behavior of a map entry is undefined if the backing map has been
872  * modified after the entry was returned by the iterator, except through
873  * the <tt>setValue</tt> operation on the map entry.
874  *
875  * @see Map#entrySet()
876  * @since 1.2
877  */
878 interface MapEntry(K,V) : IObject {
879     /**
880      * Returns the key corresponding to this entry.
881      *
882      * @return the key corresponding to this entry
883      * @throws IllegalStateException implementations may, but are not
884      *         required to, throw this exception if the entry has been
885      *         removed from the backing map.
886      */
887     K getKey();
888 
889     /**
890      * Returns the value corresponding to this entry.  If the mapping
891      * has been removed from the backing map (by the iterator's
892      * <tt>remove</tt> operation), the results of this call are undefined.
893      *
894      * @return the value corresponding to this entry
895      * @throws IllegalStateException implementations may, but are not
896      *         required to, throw this exception if the entry has been
897      *         removed from the backing map.
898      */
899     V getValue();
900 
901     /**
902      * Replaces the value corresponding to this entry with the specified
903      * value (optional operation).  (Writes through to the map.)  The
904      * behavior of this call is undefined if the mapping has already been
905      * removed from the map (by the iterator's <tt>remove</tt> operation).
906      *
907      * @param value new value to be stored in this entry
908      * @return old value corresponding to the entry
909      * @throws UnsupportedOperationException if the <tt>put</tt> operation
910      *         is not supported by the backing map
911      * @throws ClassCastException if the class of the specified value
912      *         prevents it from being stored in the backing map
913      * @throws NullPointerException if the backing map does not permit
914      *         null values, and the specified value is null
915      * @throws IllegalArgumentException if some property of this value
916      *         prevents it from being stored in the backing map
917      * @throws IllegalStateException implementations may, but are not
918      *         required to, throw this exception if the entry has been
919      *         removed from the backing map.
920      */
921     V setValue(V value);
922 
923     /**
924      * Compares the specified object with this entry for equality.
925      * Returns <tt>true</tt> if the given object is also a map entry and
926      * the two entries represent the same mapping.  More formally, two
927      * entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping
928      * if<pre>
929      *     (e1.getKey()==null ?
930      *      e2.getKey()==null : e1.getKey().equals(e2.getKey()))  &amp;&amp;
931      *     (e1.getValue()==null ?
932      *      e2.getValue()==null : e1.getValue().equals(e2.getValue()))
933      * </pre>
934      * This ensures that the <tt>equals</tt> method works properly across
935      * different implementations of the <tt>MapEntry</tt> interface.
936      *
937      * @param o object to be compared for equality with this map entry
938      * @return <tt>true</tt> if the specified object is equal to this map
939      *         entry
940      */
941     // bool opEquals(MapEntry!(K,V) o);
942 
943     /**
944      * Returns the hash code value for this map entry.  The hash code
945      * of a map entry <tt>e</tt> is defined to be: <pre>
946      *     (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
947      *     (e.getValue()==null ? 0 : e.getValue().hashCode())
948      * </pre>
949      * This ensures that <tt>e1.equals(e2)</tt> implies that
950      * <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries
951      * <tt>e1</tt> and <tt>e2</tt>, as required by the general
952      * contract of <tt>Object.hashCode</tt>.
953      *
954      * @return the hash code value for this map entry
955      * @see Object#hashCode()
956      * @see Object#equals(Object)
957      * @see #equals(Object)
958      */
959     // size_t toHash() @trusted nothrow;
960 
961     /**
962      * Returns a comparator that compares {@link MapEntry} in natural order on key.
963      *
964      * <p>The returned comparator is serializable and throws {@link
965      * NullPointerException} when comparing an entry with a null key.
966      *
967      * @param  !K the {@link Comparable} type of then map keys
968      * @param  !V the type of the map values
969      * @return a comparator that compares {@link MapEntry} in natural order on key.
970      * @see Comparable
971      * @since 1.8
972      */
973     // public static <K extends Comparable<K>, V> Comparator<MapEntry!(K,V)> comparingByKey() {
974     //     return (Comparator<MapEntry<K, V>> & Serializable)
975     //         (c1, c2) -> c1.getKey().compareTo(c2.getKey());
976     // }
977 
978     /**
979      * Returns a comparator that compares {@link MapEntry} in natural order on value.
980      *
981      * <p>The returned comparator is serializable and throws {@link
982      * NullPointerException} when comparing an entry with null values.
983      *
984      * @param !K the type of the map keys
985      * @param !V the {@link Comparable} type of the map values
986      * @return a comparator that compares {@link MapEntry} in natural order on value.
987      * @see Comparable
988      * @since 1.8
989      */
990     // public static <K, V extends Comparable<V>> Comparator<MapEntry!(K,V)> comparingByValue() {
991     //     return (Comparator<MapEntry<K, V>> & Serializable)
992     //         (c1, c2) -> c1.getValue().compareTo(c2.getValue());
993     // }
994 
995     /**
996      * Returns a comparator that compares {@link MapEntry} by key using the given
997      * {@link Comparator}.
998      *
999      * <p>The returned comparator is serializable if the specified comparator
1000      * is also serializable.
1001      *
1002      * @param  !K the type of the map keys
1003      * @param  !V the type of the map values
1004      * @param  cmp the key {@link Comparator}
1005      * @return a comparator that compares {@link MapEntry} by the key.
1006      * @since 1.8
1007      */
1008     // public static <K, V> Comparator<MapEntry<K, V>> comparingByKey(Comparator<K> cmp) {
1009     //     Objects.requireNonNull(cmp);
1010     //     return (Comparator<MapEntry<K, V>> & Serializable)
1011     //         (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
1012     // }
1013 
1014     /**
1015      * Returns a comparator that compares {@link MapEntry} by value using the given
1016      * {@link Comparator}.
1017      *
1018      * <p>The returned comparator is serializable if the specified comparator
1019      * is also serializable.
1020      *
1021      * @param  !K the type of the map keys
1022      * @param  !V the type of the map values
1023      * @param  cmp the value {@link Comparator}
1024      * @return a comparator that compares {@link MapEntry} by the value.
1025      * @since 1.8
1026      */
1027     // public static <K, V> Comparator<MapEntry<K, V>> comparingByValue(Comparator<V> cmp) {
1028     //     Objects.requireNonNull(cmp);
1029     //     return (Comparator<MapEntry<K, V>> & Serializable)
1030     //         (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
1031     // }
1032 }