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.AbstractMap;
13 
14 import hunt.collection.Collection;
15 import hunt.collection.Iterator;
16 import hunt.collection.Map;
17 import hunt.collection.Set;
18 
19 import hunt.Exceptions;
20 import hunt.Object;
21 import hunt.util.ObjectUtils;
22 
23 import std.array;
24 // import std.container.array;
25 import hunt.collection.Array;
26 import std.conv;
27 import std.exception;
28 import std.range;
29 
30 /**
31 */
32 abstract class AbstractMap(K, V) : Map!(K, V) {
33 
34     /**
35      * The number of key-value mappings contained in this map.
36      */
37     protected int _size;
38 
39     /**
40      * Sole constructor.  (For invocation by subclass constructors, typically
41      * implicit.)
42      */
43     protected this() {
44     }
45 
46     // Query Operations
47 
48     /**
49      * Returns the number of key-value mappings in this map.
50      *
51      * @return the number of key-value mappings in this map
52      */
53     int size() {
54         return _size;
55     }
56 
57     /**
58      * Returns <tt>true</tt> if this map contains no key-value mappings.
59      *
60      * @return <tt>true</tt> if this map contains no key-value mappings
61      */
62     bool isEmpty() {
63         return _size == 0;
64     }
65 
66     /**
67      * {@inheritDoc}
68      *
69      * @implSpec
70      * This implementation iterates over <tt>entrySet()</tt> searching
71      * for an entry with the specified value.  If such an entry is found,
72      * <tt>true</tt> is returned.  If the iteration terminates without
73      * finding such an entry, <tt>false</tt> is returned.  Note that this
74      * implementation requires linear time in the size of the map.
75      *
76      * @throws ClassCastException   {@inheritDoc}
77      * @throws NullPointerException {@inheritDoc}
78      */
79     bool containsKey(K key) {
80         throw new UnsupportedOperationException();
81     }
82     /**
83      * {@inheritDoc}
84      *
85      * @implSpec
86      * This implementation iterates over <tt>entrySet()</tt> searching
87      * for an entry with the specified value.  If such an entry is found,
88      * <tt>true</tt> is returned.  If the iteration terminates without
89      * finding such an entry, <tt>false</tt> is returned.  Note that this
90      * implementation requires linear time in the size of the map.
91      *
92      * @throws ClassCastException   {@inheritDoc}
93      * @throws NullPointerException {@inheritDoc}
94      */
95     bool containsValue(V value) {
96         throw new UnsupportedOperationException();
97     }
98 
99     V opIndex(K key) {
100         return get(key);
101     }
102 
103     /**
104      * {@inheritDoc}
105      *
106      * @implSpec
107      * This implementation iterates over <tt>entrySet()</tt> searching
108      * for an entry with the specified key.  If such an entry is found,
109      * the entry's value is returned.  If the iteration terminates without
110      * finding such an entry, <tt>null</tt> is returned.  Note that this
111      * implementation requires linear time in the size of the map; many
112      * implementations will override this method.
113      *
114      * @throws ClassCastException            {@inheritDoc}
115      * @throws NullPointerException          {@inheritDoc}
116      */
117     V get(K key) {
118         throw new UnsupportedOperationException();
119     }
120 
121     /**
122      * Returns the value to which the specified key is mapped, or
123      * {@code defaultValue} if this map contains no mapping for the key.
124      *
125      * @implSpec
126      * The default implementation makes no guarantees about synchronization
127      * or atomicity properties of this method. Any implementation providing
128      * atomicity guarantees must override this method and document its
129      * concurrency properties.
130      *
131      * @param key the key whose associated value is to be returned
132      * @param defaultValue the default mapping of the key
133      * @return the value to which the specified key is mapped, or
134      * {@code defaultValue} if this map contains no mapping for the key
135      * @throws ClassCastException if the key is of an inappropriate type for
136      * this map
137      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
138      * @throws NullPointerException if the specified key is null and this map
139      * does not permit null keys
140      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
141      */
142     V getOrDefault(K k, V value) {
143         throw new UnsupportedOperationException();
144     }
145 
146     // Modification Operations
147 
148     /**
149      * {@inheritDoc}
150      *
151      * @implSpec
152      * This implementation always throws an
153      * <tt>UnsupportedOperationException</tt>.
154      *
155      * @throws UnsupportedOperationException {@inheritDoc}
156      * @throws ClassCastException            {@inheritDoc}
157      * @throws NullPointerException          {@inheritDoc}
158      * @throws IllegalArgumentException      {@inheritDoc}
159      */
160     V put(K key, V value) {
161         throw new UnsupportedOperationException();
162     }
163 
164     V putIfAbsent(K key, V value) {
165         V v = V.init;
166 
167         if (!containsKey(key))
168             v = put(key, value);
169 
170         return v;
171     }
172 
173     /**
174      * {@inheritDoc}
175      *
176      * @implSpec
177      * This implementation iterates over <tt>entrySet()</tt> searching for an
178      * entry with the specified key.  If such an entry is found, its value is
179      * obtained with its <tt>getValue</tt> operation, the entry is removed
180      * from the collection (and the backing map) with the iterator's
181      * <tt>remove</tt> operation, and the saved value is returned.  If the
182      * iteration terminates without finding such an entry, <tt>null</tt> is
183      * returned.  Note that this implementation requires linear time in the
184      * size of the map; many implementations will override this method.
185      *
186      * <p>Note that this implementation throws an
187      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
188      * iterator does not support the <tt>remove</tt> method and this map
189      * contains a mapping for the specified key.
190      *
191      * @throws UnsupportedOperationException {@inheritDoc}
192      * @throws ClassCastException            {@inheritDoc}
193      * @throws NullPointerException          {@inheritDoc}
194      */
195     V remove(K key) {
196         throw new UnsupportedOperationException();
197     }
198 
199     bool remove(K key, V value) {
200         V curValue = get(key);
201         if (curValue != value || !containsKey(key))
202             return false;
203         remove(key);
204         return true;
205     }
206 
207     // Bulk Operations
208 
209     /**
210      * {@inheritDoc}
211      *
212      * @implSpec
213      * This implementation iterates over the specified map's
214      * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
215      * operation once for each entry returned by the iteration.
216      *
217      * <p>Note that this implementation throws an
218      * <tt>UnsupportedOperationException</tt> if this map does not support
219      * the <tt>put</tt> operation and the specified map is nonempty.
220      *
221      * @throws UnsupportedOperationException {@inheritDoc}
222      * @throws ClassCastException            {@inheritDoc}
223      * @throws NullPointerException          {@inheritDoc}
224      * @throws IllegalArgumentException      {@inheritDoc}
225      */
226     void putAll(Map!(K, V) m) {
227         foreach (K k, V v; m)
228             put(k, v);
229     }
230 
231     /**
232      * {@inheritDoc}
233      *
234      * @implSpec
235      * This implementation calls <tt>entrySet().clear()</tt>.
236      *
237      * <p>Note that this implementation throws an
238      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
239      * does not support the <tt>clear</tt> operation.
240      *
241      * @throws UnsupportedOperationException {@inheritDoc}
242      */
243     void clear() {
244         throw new NotImplementedException("");
245     }
246 
247     bool replace(K key, V oldValue, V newValue) {
248         V curValue = get(key);
249         if (curValue != oldValue || !containsKey(key)) {
250             return false;
251         }
252         put(key, newValue);
253         return true;
254     }
255 
256     V replace(K key, V value) {
257         V curValue = V.init;
258         if (containsKey(key)) {
259             curValue = put(key, value);
260         }
261         return curValue;
262     }
263 
264     int opApply(scope int delegate(ref K, ref V) dg) {
265         throw new NotImplementedException();
266     }
267 
268     int opApply(scope int delegate(MapEntry!(K, V) entry) dg) {
269         throw new NotImplementedException();
270     }
271 
272     InputRange!K byKey() {
273         throw new NotImplementedException();
274     }
275 
276     InputRange!V byValue() {
277         throw new NotImplementedException();
278     }
279 
280     // Views
281 
282     /**
283      * Each of these fields are initialized to contain an instance of the
284      * appropriate view the first time this view is requested.  The views are
285      * stateless, so there's no reason to create more than one of each.
286      *
287      * <p>Since there is no synchronization performed while accessing these fields,
288      * it is expected that java.util.Map view classes using these fields have
289      * no non-final fields (or any fields at all except for outer-this). Adhering
290      * to this rule would make the races on these fields benign.
291      *
292      * <p>It is also imperative that implementations read the field only once,
293      * as in:
294      *
295      * <pre> {@code
296      * public Set!K keySet() {
297      *   Set!K ks = keySet;  // single racy read
298      *   if (ks is null) {
299      *     ks = new KeySet();
300      *     keySet = ks;
301      *   }
302      *   return ks;
303      * }
304      *}</pre>
305      */
306     // protected Set!K       _keySet;
307     // protected Collection!V _values;
308 
309     /**
310      * {@inheritDoc}
311      *
312      * @implSpec
313      * This implementation returns a set that subclasses {@link AbstractSet}.
314      * The subclass's iterator method returns a "wrapper object" over this
315      * map's <tt>entrySet()</tt> iterator.  The <tt>size</tt> method
316      * delegates to this map's <tt>size</tt> method and the
317      * <tt>contains</tt> method delegates to this map's
318      * <tt>containsKey</tt> method.
319      *
320      * <p>The set is created the first time this method is called,
321      * and returned in response to all subsequent calls.  No synchronization
322      * is performed, so there is a slight chance that multiple calls to this
323      * method will not all return the same set.
324      */
325     K[] keySet() {
326         Array!K arr;
327         foreach (K key; byKey()) {
328             arr.insertBack(key);
329         }
330         return arr.array();
331     }
332 
333     /**
334      * {@inheritDoc}
335      *
336      * @implSpec
337      * This implementation returns a collection that subclasses {@link
338      * AbstractCollection}.  The subclass's iterator method returns a
339      * "wrapper object" over this map's <tt>entrySet()</tt> iterator.
340      * The <tt>size</tt> method delegates to this map's <tt>size</tt>
341      * method and the <tt>contains</tt> method delegates to this map's
342      * <tt>containsValue</tt> method.
343      *
344      * <p>The collection is created the first time this method is called, and
345      * returned in response to all subsequent calls.  No synchronization is
346      * performed, so there is a slight chance that multiple calls to this
347      * method will not all return the same collection.
348      */
349     V[] values() {
350         // FIXME: Needing refactor or cleanup -@zxp at 9/26/2018, 6:00:54 PM
351         // 
352         // Array!V arr;
353         // foreach(V value; byValue()) {
354         //     arr.insertBack(value);
355         // }
356         // return arr.array();
357         // V[] arr;
358         // foreach (V value; byValue()) {
359         //     arr ~= value;
360         // }
361         // return arr;
362         return byValue().array();
363     }
364 
365     // Comparison and hashing
366 
367     /**
368      * Compares the specified object with this map for equality.  Returns
369      * <tt>true</tt> if the given object is also a map and the two maps
370      * represent the same mappings.  More formally, two maps <tt>m1</tt> and
371      * <tt>m2</tt> represent the same mappings if
372      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
373      * <tt>equals</tt> method works properly across different implementations
374      * of the <tt>Map</tt> interface.
375      *
376      * @implSpec
377      * This implementation first checks if the specified object is this map;
378      * if so it returns <tt>true</tt>.  Then, it checks if the specified
379      * object is a map whose size is identical to the size of this map; if
380      * not, it returns <tt>false</tt>.  If so, it iterates over this map's
381      * <tt>entrySet</tt> collection, and checks that the specified map
382      * contains each mapping that this map contains.  If the specified map
383      * fails to contain such a mapping, <tt>false</tt> is returned.  If the
384      * iteration completes, <tt>true</tt> is returned.
385      *
386      * @param o object to be compared for equality with this map
387      * @return <tt>true</tt> if the specified object is equal to this map
388      */
389     override bool opEquals(Object o) {
390         if(o is null) return false;
391         if (o is this) return true;
392 
393         auto m = cast(Map!(K, V)) o;
394         if(m is null) return false;
395         if (m.size() != size())
396             return false;
397 
398         try {
399             foreach(K key, V value; this) {
400                 if(value != m.get(key))
401                     return false;
402             }
403         } catch (Exception) {
404             return false;
405         }
406 
407         return true;
408     }
409 
410     bool opEquals(IObject o) {
411         return opEquals(cast(Object) o);
412     }
413 
414     // Iterator!(MapEntry!(K,V)) iterator()
415     // {
416     //     throw new UnsupportedOperationException();
417     // }
418 
419     /**
420      * Returns the hash code value for this map.  The hash code of a map is
421      * defined to be the sum of the hash codes of each entry in the map's
422      * <tt>entrySet()</tt> view.  This ensures that <tt>m1.equals(m2)</tt>
423      * implies that <tt>m1.toHash()==m2.toHash()</tt> for any two maps
424      * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
425      * {@link Object#toHash}.
426      *
427      * @implSpec
428      * This implementation iterates over <tt>entrySet()</tt>, calling
429      * {@link MapEntry#toHash toHash()} on each element (entry) in the
430      * set, and adding up the results.
431      *
432      * @return the hash code value for this map
433      * @see MapEntry#toHash()
434      * @see Object#equals(Object)
435      * @see Set#equals(Object)
436      */
437     override size_t toHash() @trusted nothrow {
438         size_t h = 0;
439         try {
440             foreach (MapEntry!(K, V) i; this) {
441                 h += i.toHash();
442             }
443         } catch (Exception ex) {
444         }
445         return h;
446     }
447 
448     /**
449      * Returns a string representation of this map.  The string representation
450      * consists of a list of key-value mappings in the order returned by the
451      * map's <tt>entrySet</tt> view's iterator, enclosed in braces
452      * (<tt>"{}"</tt>).  Adjacent mappings are separated by the characters
453      * <tt>", "</tt> (comma and space).  Each key-value mapping is rendered as
454      * the key followed by an equals sign (<tt>"="</tt>) followed by the
455      * associated value.  Keys and values are converted to strings as by
456      * {@link string#valueOf(Object)}.
457      *
458      * @return a string representation of this map
459      */
460     override string toString() {
461         if (isEmpty())
462             return "{}";
463 
464         Appender!string sb;
465         sb.put("{");
466         bool isFirst = true;
467         foreach (K key, V value; this) {
468             if (!isFirst) {
469                 sb.put(", ");
470             }
471             sb.put(key.to!string() ~ "=" ~ value.to!string());
472             isFirst = false;
473         }
474         sb.put("}");
475 
476         return sb.data;
477     }
478 
479     /**
480      * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys
481      * and values themselves are not cloned.
482      *
483      * @return a shallow copy of this map
484      */
485     mixin CloneMemberTemplate!(typeof(this));
486 
487     /**
488      * Utility method for SimpleEntry and SimpleImmutableEntry.
489      * Test for equality, checking for nulls.
490      *
491      * NB: Do not replace with Object.equals until JDK-8015417 is resolved.
492      */
493     // private static bool eq(Object o1, Object o2) {
494     //     return o1 is null ? o2 is null : o1.equals(o2);
495     // }
496 
497 }
498 
499 /**
500 * An Entry maintaining an immutable key and value.  This class
501 * does not support method <tt>setValue</tt>.  This class may be
502 * convenient in methods that return thread-safe snapshots of
503 * key-value mappings.
504 *
505 * @since 1.6
506 */
507 class SimpleImmutableEntry(K, V) : AbstractMapEntry!(K, V) {
508 
509     /**
510         * Creates an entry representing a mapping from the specified
511         * key to the specified value.
512         *
513         * @param key the key represented by this entry
514         * @param value the value represented by this entry
515         */
516     this(K key, V value) {
517         super(key, value);
518     }
519 
520     /**
521         * Creates an entry representing the same mapping as the
522         * specified entry.
523         *
524         * @param entry the entry to copy
525         */
526     this(MapEntry!(K, V) entry) {
527         super(entry.getKey(), entry.getValue());
528     }
529 
530     /**
531     * Replaces the value corresponding to this entry with the specified
532     * value (optional operation).  This implementation simply throws
533     * <tt>UnsupportedOperationException</tt>, as this class implements
534     * an <i>immutable</i> map entry.
535     *
536     * @param value new value to be stored in this entry
537     * @return (Does not return)
538     * @throws UnsupportedOperationException always
539     */
540     override V setValue(V value) {
541         throw new UnsupportedOperationException();
542     }
543 
544     /**
545      * Returns the hash code value for this map entry.  The hash code
546      * of a map entry {@code e} is defined to be: <pre>
547      *   (e.getKey()==null   ? 0 : e.getKey().toHash()) ^
548      *   (e.getValue()==null ? 0 : e.getValue().toHash())</pre>
549      * This ensures that {@code e1.equals(e2)} implies that
550      * {@code e1.toHash()==e2.toHash()} for any two Entries
551      * {@code e1} and {@code e2}, as required by the general
552      * contract of {@link Object#toHash}.
553      *
554      * @return the hash code value for this map entry
555      * @see    #equals
556      */
557     override size_t toHash() @trusted nothrow {
558         static if (is(K == class)) {
559             size_t kHash = 0;
560             if (key !is null)
561                 kHash = key.toHash();
562         } else {
563             size_t kHash = hashOf(key);
564         }
565 
566         static if (is(V == class)) {
567             size_t vHash = 0;
568             if (value !is null)
569                 vHash = value.toHash();
570         } else {
571             size_t vHash = hashOf(value);
572         }
573 
574         return kHash ^ vHash;
575     }
576 }
577 
578 /**
579 */
580 class EmptyMap(K, V) : AbstractMap!(K, V) {
581 
582     override int size() {
583         return 0;
584     }
585 
586     override bool isEmpty() {
587         return true;
588     }
589 
590     override bool containsKey(K key) {
591         return false;
592     }
593 
594     // override
595     // bool containsValue(V value) {return false;}
596 
597     override V get(K key) {
598         return V.init;
599     }
600 
601     override K[] keySet() {
602         return null;
603     }
604 
605     override V[] values() {
606         return null;
607     }
608     // Collection!(V) values()              {return emptySet();}
609     // Set!(MapEntry!(K,V)) entrySet()      {return emptySet();}
610 
611     override bool opEquals(Object o) {
612         return (typeid(o) == typeid(Map!(K, V))) && (cast(Map!(K, V)) o).isEmpty();
613     }
614 
615     override size_t toHash() {
616         return 0;
617     }
618 
619     // Override default methods in Map
620     override V getOrDefault(K k, V defaultValue) {
621         return defaultValue;
622     }
623 
624     override int opApply(scope int delegate(ref K, ref V) dg) {
625         return 0;
626     }
627 
628     // override
629     // void replaceAll(BiFunction!(K, V, V) function) {
630     //     Objects.requireNonNull(function);
631     // }
632 
633     // override
634     // V putIfAbsent(K key, V value) {
635     //     throw new UnsupportedOperationException();
636     // }
637 
638     // override
639     // bool remove(Object key, Object value) {
640     //     throw new UnsupportedOperationException();
641     // }
642 
643     // override
644     // bool replace(K key, V oldValue, V newValue) {
645     //     throw new UnsupportedOperationException();
646     // }
647 
648     // override
649     // V replace(K key, V value) {
650     //     throw new UnsupportedOperationException();
651     // }
652 
653     // override
654     // V computeIfAbsent(K key,
655     //         Function!(K, V) mappingFunction) {
656     //     throw new UnsupportedOperationException();
657     // }
658 
659     // override
660     // V computeIfPresent(K key,
661     //         BiFunction!(K, V, V) remappingFunction) {
662     //     throw new UnsupportedOperationException();
663     // }
664 
665     // override
666     // V compute(K key,
667     //         BiFunction!(K, V, V) remappingFunction) {
668     //     throw new UnsupportedOperationException();
669     // }
670 
671     // override
672     // V merge(K key, V value,
673     //         BiFunction!(V, V, V) remappingFunction) {
674     //     throw new UnsupportedOperationException();
675     // }
676 
677     // // Preserves singleton property
678     // private Object readResolve() {
679     //     return EMPTY_MAP;
680     // }
681 }
682 
683 
684 
685 // Implementation Note: SimpleEntry and SimpleImmutableEntry
686 // are distinct unrelated classes, even though they share
687 // some code. Since you can't add or subtract final-ness
688 // of a field in a subclass, they can't share representations,
689 // and the amount of duplicated code is too small to warrant
690 // exposing a common abstract class.
691 
692 /**
693  * An Entry maintaining a key and a value.  The value may be
694  * changed using the <tt>setValue</tt> method.  This class
695  * facilitates the process of building custom map
696  * implementations. For example, it may be convenient to return
697  * arrays of <tt>SimpleEntry</tt> instances in method
698  * <tt>Map.entrySet().toArray</tt>.
699  *
700  */
701 class SimpleEntry(K,V) : MapEntry!(K,V)
702 {
703 
704     private K key;
705     private V value;
706 
707     /**
708      * Creates an entry representing a mapping from the specified
709      * key to the specified value.
710      *
711      * @param key the key represented by this entry
712      * @param value the value represented by this entry
713      */
714     this(K key, V value) {
715         this.key   = key;
716         this.value = value;
717     }
718 
719     /**
720      * Creates an entry representing the same mapping as the
721      * specified entry.
722      *
723      * @param entry the entry to copy
724      */
725     this(MapEntry!(K, V) entry) {
726         this.key   = entry.getKey();
727         this.value = entry.getValue();
728     }
729 
730     /**
731      * Returns the key corresponding to this entry.
732      *
733      * @return the key corresponding to this entry
734      */
735     K getKey() {
736         return key;
737     }
738 
739     /**
740      * Returns the value corresponding to this entry.
741      *
742      * @return the value corresponding to this entry
743      */
744     V getValue() {
745         return value;
746     }
747 
748     /**
749      * Replaces the value corresponding to this entry with the specified
750      * value.
751      *
752      * @param value new value to be stored in this entry
753      * @return the old value corresponding to the entry
754      */
755     V setValue(V value) {
756         V oldValue = this.value;
757         this.value = value;
758         return oldValue;
759     }
760 
761     /**
762      * Compares the specified object with this entry for equality.
763      * Returns {@code true} if the given object is also a map entry and
764      * the two entries represent the same mapping.  More formally, two
765      * entries {@code e1} and {@code e2} represent the same mapping
766      * if<pre>
767      *   (e1.getKey()==null ?
768      *    e2.getKey()==null :
769      *    e1.getKey().equals(e2.getKey()))
770      *   &amp;&amp;
771      *   (e1.getValue()==null ?
772      *    e2.getValue()==null :
773      *    e1.getValue().equals(e2.getValue()))</pre>
774      * This ensures that the {@code equals} method works properly across
775      * different implementations of the {@code MapEntry} interface.
776      *
777      * @param o object to be compared for equality with this map entry
778      * @return {@code true} if the specified object is equal to this map
779      *         entry
780      * @see    #toHash
781      */
782     override bool opEquals(Object o) {
783         if (o is this)
784             return true;
785             
786         MapEntry!(K, V) e = cast(MapEntry!(K, V))o;
787         if (e !is null) {
788             if (key == e.getKey() && value == e.getValue())
789                 return true;
790         }
791         return false;   
792     }
793 
794     bool opEquals(IObject o) {
795         return opEquals(cast(Object) o);
796     }
797 
798     int opCmp(MapEntry!(K, V) o) {
799         throw new NotImplementedException("opCmp");
800     }
801 
802     alias opCmp = Object.opCmp;
803 
804     /**
805      * Returns the hash code value for this map entry.  The hash code
806      * of a map entry {@code e} is defined to be: <pre>
807      *   (e.getKey()==null   ? 0 : e.getKey().toHash()) ^
808      *   (e.getValue()==null ? 0 : e.getValue().toHash())</pre>
809      * This ensures that {@code e1.equals(e2)} implies that
810      * {@code e1.toHash()==e2.toHash()} for any two Entries
811      * {@code e1} and {@code e2}, as required by the general
812      * contract of {@link Object#toHash}.
813      *
814      * @return the hash code value for this map entry
815      * @see    #equals
816      */
817     override size_t toHash() @trusted nothrow {
818         return hashOf(key) ^ hashOf(value);
819     }
820 
821     /**
822      * Returns a string representation of this map entry.  This
823      * implementation returns the string representation of this
824      * entry's key followed by the equals character ("<tt>=</tt>")
825      * followed by the string representation of this entry's value.
826      *
827      * @return a string representation of this map entry
828      */
829     override string toString() {
830         return key.to!string() ~ "=" ~ value.to!string();
831     }
832 
833 }