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.LinkedHashMap;
13 
14 import hunt.collection.AbstractCollection;
15 import hunt.collection.AbstractMap;
16 import hunt.collection.HashMap;
17 import hunt.collection.Map;
18 
19 import hunt.Exceptions;
20 
21 import std.range;
22 
23 /**
24  * <p>Hash table and linked list implementation of the <tt>Map</tt> interface,
25  * with predictable iteration order.  This implementation differs from
26  * <tt>HashMap</tt> in that it maintains a doubly-linked list running through
27  * all of its entries.  This linked list defines the iteration ordering,
28  * which is normally the order in which keys were inserted into the map
29  * (<i>insertion-order</i>).  Note that insertion order is not affected
30  * if a key is <i>re-inserted</i> into the map.  (A key <tt>k</tt> is
31  * reinserted into a map <tt>m</tt> if <tt>m.put(k, v)</tt> is invoked when
32  * <tt>m.containsKey(k)</tt> would return <tt>true</tt> immediately prior to
33  * the invocation.)
34  *
35  * <p>This implementation spares its clients from the unspecified, generally
36  * chaotic ordering provided by {@link HashMap} (and {@link Hashtable}),
37  * without incurring the increased cost associated with {@link TreeMap}.  It
38  * can be used to produce a copy of a map that has the same order as the
39  * original, regardless of the original map's implementation:
40  * <pre>
41  *     void foo(Map m) {
42  *         Map copy = new LinkedHashMap(m);
43  *         ...
44  *     }
45  * </pre>
46  * This technique is particularly useful if a module takes a map on input,
47  * copies it, and later returns results whose order is determined by that of
48  * the copy.  (Clients generally appreciate having things returned in the same
49  * order they were presented.)
50  *
51  * <p>A special {@link #LinkedHashMap(int,float,bool) constructor} is
52  * provided to create a linked hash map whose order of iteration is the order
53  * in which its entries were last accessed, from least-recently accessed to
54  * most-recently (<i>access-order</i>).  This kind of map is well-suited to
55  * building LRU caches.  Invoking the {@code put}, {@code putIfAbsent},
56  * {@code get}, {@code getOrDefault}, {@code compute}, {@code computeIfAbsent},
57  * {@code computeIfPresent}, or {@code merge} methods results
58  * in an access to the corresponding entry (assuming it exists after the
59  * invocation completes). The {@code replace} methods only result in an access
60  * of the entry if the value is replaced.  The {@code putAll} method generates one
61  * entry access for each mapping in the specified map, in the order that
62  * key-value mappings are provided by the specified map's entry set iterator.
63  * <i>No other methods generate entry accesses.</i>  In particular, operations
64  * on collection-views do <i>not</i> affect the order of iteration of the
65  * backing map.
66  *
67  * <p>The {@link #removeEldestEntry(MapEntry)} method may be overridden to
68  * impose a policy for removing stale mappings automatically when new mappings
69  * are added to the map.
70  *
71  * <p>This class provides all of the optional <tt>Map</tt> operations, and
72  * permits null elements.  Like <tt>HashMap</tt>, it provides constant-time
73  * performance for the basic operations (<tt>add</tt>, <tt>contains</tt> and
74  * <tt>remove</tt>), assuming the hash function disperses elements
75  * properly among the buckets.  Performance is likely to be just slightly
76  * below that of <tt>HashMap</tt>, due to the added expense of maintaining the
77  * linked list, with one exception: Iteration over the collection-views
78  * of a <tt>LinkedHashMap</tt> requires time proportional to the <i>size</i>
79  * of the map, regardless of its capacity.  Iteration over a <tt>HashMap</tt>
80  * is likely to be more expensive, requiring time proportional to its
81  * <i>capacity</i>.
82  *
83  * <p>A linked hash map has two parameters that affect its performance:
84  * <i>initial capacity</i> and <i>load factor</i>.  They are defined precisely
85  * as for <tt>HashMap</tt>.  Note, however, that the penalty for choosing an
86  * excessively high value for initial capacity is less severe for this class
87  * than for <tt>HashMap</tt>, as iteration times for this class are unaffected
88  * by capacity.
89  *
90  * <p><strong>Note that this implementation is not synchronized.</strong>
91  * If multiple threads access a linked hash map concurrently, and at least
92  * one of the threads modifies the map structurally, it <em>must</em> be
93  * synchronized externally.  This is typically accomplished by
94  * synchronizing on some object that naturally encapsulates the map.
95  *
96  * If no such object exists, the map should be "wrapped" using the
97  * {@link Collections#synchronizedMap Collections.synchronizedMap}
98  * method.  This is best done at creation time, to prevent accidental
99  * unsynchronized access to the map:<pre>
100  *   Map m = Collections.synchronizedMap(new LinkedHashMap(...));</pre>
101  *
102  * A structural modification is any operation that adds or deletes one or more
103  * mappings or, in the case of access-ordered linked hash maps, affects
104  * iteration order.  In insertion-ordered linked hash maps, merely changing
105  * the value associated with a key that is already contained in the map is not
106  * a structural modification.  <strong>In access-ordered linked hash maps,
107  * merely querying the map with <tt>get</tt> is a structural modification.
108  * </strong>)
109  *
110  * <p>The iterators returned by the <tt>iterator</tt> method of the collections
111  * returned by all of this class's collection view methods are
112  * <em>fail-fast</em>: if the map is structurally modified at any time after
113  * the iterator is created, in any way except through the iterator's own
114  * <tt>remove</tt> method, the iterator will throw a {@link
115  * ConcurrentModificationException}.  Thus, in the face of concurrent
116  * modification, the iterator fails quickly and cleanly, rather than risking
117  * arbitrary, non-deterministic behavior at an undetermined time in the future.
118  *
119  * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
120  * as it is, generally speaking, impossible to make any hard guarantees in the
121  * presence of unsynchronized concurrent modification.  Fail-fast iterators
122  * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
123  * Therefore, it would be wrong to write a program that depended on this
124  * exception for its correctness:   <i>the fail-fast behavior of iterators
125  * should be used only to detect bugs.</i>
126  *
127  * <p>The spliterators returned by the spliterator method of the collections
128  * returned by all of this class's collection view methods are
129  * <em><a href="Spliterator.html#binding">late-binding</a></em>,
130  * <em>fail-fast</em>, and additionally report {@link Spliterator#ORDERED}.
131  *
132  * <p>This class is a member of the
133  * <a href="{@docRoot}/../technotes/guides/collections/index.html">
134  * Java Collections Framework</a>.
135  *
136  * @implNote
137  * The spliterators returned by the spliterator method of the collections
138  * returned by all of this class's collection view methods are created from
139  * the iterators of the corresponding collections.
140  *
141  * @param (K) the type of keys maintained by this map
142  * @param (V) the type of mapped values
143  *
144  * @author  Josh Bloch
145  * @see     Object#hashCode()
146  * @see     Collection
147  * @see     Map
148  * @see     HashMap
149  * @see     TreeMap
150  * @see     Hashtable
151  */
152 class LinkedHashMap(K, V) : HashMap!(K, V)
153 {
154     /*
155      * Implementation note.  A previous version of this class was
156      * internally structured a little differently. Because superclass
157      * HashMap now uses trees for some of its nodes, class
158      * LinkedHashMapEntry is now treated as intermediary node class
159      * that can also be converted to tree form. The name of this
160      * class, LinkedHashMapEntry, is confusing in several ways in its
161      * current context, but cannot be changed.  Otherwise, even though
162      * it is not exported outside this package, some existing source
163      * code is known to have relied on a symbol resolution corner case
164      * rule in calls to removeEldestEntry that suppressed compilation
165      * errors due to ambiguous usages. So, we keep the name to
166      * preserve unmodified compilability.
167      *
168      * The changes in node classes also require using two fields
169      * (head, tail) rather than a pointer to a header node to maintain
170      * the doubly-linked before/after list. This class also
171      * previously used a different style of callback methods upon
172      * access, insertion, and removal.
173      */
174 
175 
176     // private static final long serialVersionUID = 3801124242820219131L;
177 
178     /**
179      * The head (eldest) of the doubly linked list.
180      */
181     LinkedHashMapEntry!(K, V) head;
182 
183     /**
184      * The tail (youngest) of the doubly linked list.
185      */
186     LinkedHashMapEntry!(K, V) tail;
187 
188     /**
189      * The iteration ordering method for this linked hash map: <tt>true</tt>
190      * for access-order, <tt>false</tt> for insertion-order.
191      *
192      * @serial
193      */
194     bool accessOrder;
195 
196     // internal utilities
197 
198     // link at the end of list
199     private void linkNodeLast(LinkedHashMapEntry!(K, V) p) {
200         LinkedHashMapEntry!(K, V) last = tail;
201         tail = p;
202         if (last is null)
203             head = p;
204         else {
205             p.before = last;
206             last.after = p;
207         }
208     }
209 
210     // apply src's links to dst
211     private void transferLinks(LinkedHashMapEntry!(K, V) src,
212                                LinkedHashMapEntry!(K, V) dst) {
213         LinkedHashMapEntry!(K, V) b = dst.before = src.before;
214         LinkedHashMapEntry!(K, V) a = dst.after = src.after;
215         if (b is null)
216             head = dst;
217         else
218             b.after = dst;
219         if (a is null)
220             tail = dst;
221         else
222             a.before = dst;
223     }
224 
225     // overrides of HashMap hook methods
226 
227     override void reinitialize() {
228         super.reinitialize();
229         head = tail = null;
230     }
231 
232     override HashMapNode!(K, V) newNode(size_t hash, K key, V value, HashMapNode!(K, V) e) {
233         LinkedHashMapEntry!(K, V) p = new LinkedHashMapEntry!(K, V)(hash, key, value, e);
234         linkNodeLast(p);
235         return p;
236     }
237 
238     override HashMapNode!(K, V) replacementNode(HashMapNode!(K, V) p, HashMapNode!(K, V) next) {
239         LinkedHashMapEntry!(K, V) q = cast(LinkedHashMapEntry!(K, V))p;
240         LinkedHashMapEntry!(K, V) t = new LinkedHashMapEntry!(K, V)(q.hash, q.key, q.value, next);
241         transferLinks(q, t);
242         return t;
243     }
244 
245     override TreeNode!(K, V) newTreeNode(size_t hash, K key, V value, HashMapNode!(K, V) next) {
246         TreeNode!(K, V) p = new TreeNode!(K, V)(hash, key, value, next);
247         linkNodeLast(p);
248         return p;
249     }
250 
251     override TreeNode!(K, V) replacementTreeNode(HashMapNode!(K, V) p, HashMapNode!(K, V) next) {
252         LinkedHashMapEntry!(K, V)q = cast(LinkedHashMapEntry!(K, V))p;
253         TreeNode!(K, V) t = new TreeNode!(K, V)(q.hash, q.key, q.value, next);
254         transferLinks(q, t);
255         return t;
256     }
257 
258     override void afterNodeRemoval(HashMapNode!(K, V) e) { // unlink
259         LinkedHashMapEntry!(K, V) p =
260             cast(LinkedHashMapEntry!(K, V))e, b = p.before, a = p.after;
261         p.before = p.after = null;
262         if (b is null)
263             head = a;
264         else
265             b.after = a;
266         if (a is null)
267             tail = b;
268         else
269             a.before = b;
270     }
271 
272     override void afterNodeInsertion(bool evict) { // possibly remove eldest
273         LinkedHashMapEntry!(K, V) first;
274         if (evict && (first = head) !is null && removeEldestEntry(first)) {
275             K key = first.key;
276             removeNode(hash(key), key, V.init, false, true);
277         }
278     }
279 
280     override void afterNodeAccess(HashMapNode!(K, V) e) { // move node to last
281         LinkedHashMapEntry!(K, V) last;
282         if (accessOrder && (last = tail) != e) {
283             LinkedHashMapEntry!(K, V) p =
284                 cast(LinkedHashMapEntry!(K, V))e, b = p.before, a = p.after;
285             p.after = null;
286             if (b is null)
287                 head = a;
288             else
289                 b.after = a;
290             if (a !is null)
291                 a.before = b;
292             else
293                 last = b;
294             if (last is null)
295                 head = p;
296             else {
297                 p.before = last;
298                 last.after = p;
299             }
300             tail = p;
301             ++modCount;
302         }
303     }
304 
305     // void internalWriteEntries(java.io.ObjectOutputStream s) {
306     //     for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after) {
307     //         s.writeObject(e.key);
308     //         s.writeObject(e.value);
309     //     }
310     // }
311 
312     /**
313      * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
314      * with the specified initial capacity and load factor.
315      *
316      * @param  initialCapacity the initial capacity
317      * @param  loadFactor      the load factor
318      * @throws IllegalArgumentException if the initial capacity is negative
319      *         or the load factor is nonpositive
320      */
321     this(int initialCapacity, float loadFactor) {
322         super(initialCapacity, loadFactor);
323         accessOrder = false;
324     }
325 
326     /**
327      * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
328      * with the specified initial capacity and a default load factor (0.75).
329      *
330      * @param  initialCapacity the initial capacity
331      * @throws IllegalArgumentException if the initial capacity is negative
332      */
333     this(int initialCapacity) {
334         super(initialCapacity);
335         accessOrder = false;
336     }
337 
338     /**
339      * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance
340      * with the default initial capacity (16) and load factor (0.75).
341      */
342     this() {
343         super();
344         accessOrder = false;
345     }
346 
347     /**
348      * Constructs an insertion-ordered <tt>LinkedHashMap</tt> instance with
349      * the same mappings as the specified map.  The <tt>LinkedHashMap</tt>
350      * instance is created with a default load factor (0.75) and an initial
351      * capacity sufficient to hold the mappings in the specified map.
352      *
353      * @param  m the map whose mappings are to be placed in this map
354      * @throws NullPointerException if the specified map is null
355      */
356     this(Map!(K, V) m) {
357         super();
358         accessOrder = false;
359         putMapEntries(m, false);
360     }
361 
362     /**
363      * Constructs an empty <tt>LinkedHashMap</tt> instance with the
364      * specified initial capacity, load factor and ordering mode.
365      *
366      * @param  initialCapacity the initial capacity
367      * @param  loadFactor      the load factor
368      * @param  accessOrder     the ordering mode - <tt>true</tt> for
369      *         access-order, <tt>false</tt> for insertion-order
370      * @throws IllegalArgumentException if the initial capacity is negative
371      *         or the load factor is nonpositive
372      */
373     this(int initialCapacity,
374                          float loadFactor,
375                          bool accessOrder) {
376         super(initialCapacity, loadFactor);
377         this.accessOrder = accessOrder;
378     }
379 
380 
381     /**
382      * Returns <tt>true</tt> if this map maps one or more keys to the
383      * specified value.
384      *
385      * @param value value whose presence in this map is to be tested
386      * @return <tt>true</tt> if this map maps one or more keys to the
387      *         specified value
388      */
389     override bool containsValue(V value) {
390         for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after) {
391             V v = e.value;
392             if (v == value) //  || (value !is null && value.equals(v))
393                 return true;
394         }
395         return false;
396     }
397 
398     /**
399      * Returns the value to which the specified key is mapped,
400      * or {@code null} if this map contains no mapping for the key.
401      *
402      * <p>More formally, if this map contains a mapping from a key
403      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
404      * key.equals(k))}, then this method returns {@code v}; otherwise
405      * it returns {@code null}.  (There can be at most one such mapping.)
406      *
407      * <p>A return value of {@code null} does not <i>necessarily</i>
408      * indicate that the map contains no mapping for the key; it's also
409      * possible that the map explicitly maps the key to {@code null}.
410      * The {@link #containsKey containsKey} operation may be used to
411      * distinguish these two cases.
412      */
413     override V get(K key) {
414         HashMapNode!(K, V) e = getNode(hash(key), key);
415         if (e is null) {
416             static if(is(V == class) || is(V == interface) || is(V == string)) {
417                 return null;
418             } else {
419                 throw new NoSuchElementException();
420             }
421         }
422 
423         if (accessOrder)
424             afterNodeAccess(e);
425         return e.value;
426     }
427 
428     /**
429      * {@inheritDoc}
430      */
431     override V getOrDefault(K key, V defaultValue) {
432        HashMapNode!(K, V) e;
433        if ((e = getNode(hash(key), key)) is null)
434            return defaultValue;
435        if (accessOrder)
436            afterNodeAccess(e);
437        return e.value;
438    }
439 
440     /**
441      * {@inheritDoc}
442      */
443     override void clear() {
444         super.clear();
445         head = tail = null;
446     }
447 
448     /**
449      * Returns <tt>true</tt> if this map should remove its eldest entry.
450      * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after
451      * inserting a new entry into the map.  It provides the implementor
452      * with the opportunity to remove the eldest entry each time a new one
453      * is added.  This is useful if the map represents a cache: it allows
454      * the map to reduce memory consumption by deleting stale entries.
455      *
456      * <p>Sample use: this override will allow the map to grow up to 100
457      * entries and then delete the eldest entry each time a new entry is
458      * added, maintaining a steady state of 100 entries.
459      * <pre>
460      *     private static final int MAX_ENTRIES = 100;
461      *
462      *     protected bool removeEldestEntry(MapEntry eldest) {
463      *        return size() &gt; MAX_ENTRIES;
464      *     }
465      * </pre>
466      *
467      * <p>This method typically does not modify the map in any way,
468      * instead allowing the map to modify itself as directed by its
469      * return value.  It <i>is</i> permitted for this method to modify
470      * the map directly, but if it does so, it <i>must</i> return
471      * <tt>false</tt> (indicating that the map should not attempt any
472      * further modification).  The effects of returning <tt>true</tt>
473      * after modifying the map from within this method are unspecified.
474      *
475      * <p>This implementation merely returns <tt>false</tt> (so that this
476      * map acts like a normal map - the eldest element is never removed).
477      *
478      * @param    eldest The least recently inserted entry in the map, or if
479      *           this is an access-ordered map, the least recently accessed
480      *           entry.  This is the entry that will be removed it this
481      *           method returns <tt>true</tt>.  If the map was empty prior
482      *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting
483      *           in this invocation, this will be the entry that was just
484      *           inserted; in other words, if the map contains a single
485      *           entry, the eldest entry is also the newest.
486      * @return   <tt>true</tt> if the eldest entry should be removed
487      *           from the map; <tt>false</tt> if it should be retained.
488      */
489     protected bool removeEldestEntry(MapEntry!(K, V) eldest) {
490         return false;
491     }
492 
493     /**
494      * Returns a {@link Set} view of the keys contained in this map.
495      * The set is backed by the map, so changes to the map are
496      * reflected in the set, and vice-versa.  If the map is modified
497      * while an iteration over the set is in progress (except through
498      * the iterator's own <tt>remove</tt> operation), the results of
499      * the iteration are undefined.  The set supports element removal,
500      * which removes the corresponding mapping from the map, via the
501      * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>,
502      * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt>
503      * operations.  It does not support the <tt>add</tt> or <tt>addAll</tt>
504      * operations.
505      * Its {@link Spliterator} typically provides faster sequential
506      * performance but much poorer parallel performance than that of
507      * {@code HashMap}.
508      *
509      * @return a set view of the keys contained in this map
510      */
511     // Set!(K) keySet() {
512     //     Set!(K) ks = keySet;
513     //     if (ks is null) {
514     //         ks = new LinkedKeySet();
515     //         keySet = ks;
516     //     }
517     //     return ks;
518     // }
519 
520     // final class LinkedKeySet : AbstractSet!(K) {
521     //     final int size()                 { return size; }
522     //     final void clear()               { LinkedHashMap.this.clear(); }
523     //     final Iterator!(K) iterator() {
524     //         return new LinkedKeyIterator();
525     //     }
526     //     final bool contains(Object o) { return containsKey(o); }
527     //     final bool remove(Object key) {
528     //         return removeNode(hash(key), key, null, false, true) !is null;
529     //     }
530     //     final Spliterator!(K) spliterator()  {
531     //         return Spliterators.spliterator(this, Spliterator.SIZED |
532     //                                         Spliterator.ORDERED |
533     //                                         Spliterator.DISTINCT);
534     //     }
535     //     final void forEach(Consumer<K> action) {
536     //         if (action is null)
537     //             throw new NullPointerException();
538     //         int mc = modCount;
539     //         for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after)
540     //             action.accept(e.key);
541     //         if (modCount != mc)
542     //             throw new ConcurrentModificationException();
543     //     }
544     // }
545 
546     /**
547      * Returns a {@link Collection} view of the values contained in this map.
548      * The collection is backed by the map, so changes to the map are
549      * reflected in the collection, and vice-versa.  If the map is
550      * modified while an iteration over the collection is in progress
551      * (except through the iterator's own <tt>remove</tt> operation),
552      * the results of the iteration are undefined.  The collection
553      * supports element removal, which removes the corresponding
554      * mapping from the map, via the <tt>Iterator.remove</tt>,
555      * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
556      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
557      * support the <tt>add</tt> or <tt>addAll</tt> operations.
558      * Its {@link Spliterator} typically provides faster sequential
559      * performance but much poorer parallel performance than that of
560      * {@code HashMap}.
561      *
562      * @return a view of the values contained in this map
563      */
564     // Collection!(V) values() {
565     //     Collection!(V) vs = values;
566     //     if (vs is null) {
567     //         vs = new LinkedValues();
568     //         values = vs;
569     //     }
570     //     return vs;
571     // }
572 
573     // final class LinkedValues : AbstractCollection!(V) {
574     //     final override int size()                 { return _size; }
575     //     final override void clear()               { this.outer.clear(); }
576     //     // final Iterator!(V) iterator() {
577     //     //     return new LinkedValueIterator();
578     //     // }
579     //     final bool contains(Object o) { return containsValue(o); }
580     //     // final Spliterator!(V) spliterator() {
581     //     //     return Spliterators.spliterator(this, Spliterator.SIZED |
582     //     //                                     Spliterator.ORDERED);
583     //     // }
584     //     // final void forEach(Consumer<V> action) {
585     //     //     if (action is null)
586     //     //         throw new NullPointerException();
587     //     //     int mc = modCount;
588     //     //     for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after)
589     //     //         action.accept(e.value);
590     //     //     if (modCount != mc)
591     //     //         throw new ConcurrentModificationException();
592     //     // }
593     // }
594 
595     /**
596      * Returns a {@link Set} view of the mappings contained in this map.
597      * The set is backed by the map, so changes to the map are
598      * reflected in the set, and vice-versa.  If the map is modified
599      * while an iteration over the set is in progress (except through
600      * the iterator's own <tt>remove</tt> operation, or through the
601      * <tt>setValue</tt> operation on a map entry returned by the
602      * iterator) the results of the iteration are undefined.  The set
603      * supports element removal, which removes the corresponding
604      * mapping from the map, via the <tt>Iterator.remove</tt>,
605      * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
606      * <tt>clear</tt> operations.  It does not support the
607      * <tt>add</tt> or <tt>addAll</tt> operations.
608      * Its {@link Spliterator} typically provides faster sequential
609      * performance but much poorer parallel performance than that of
610      * {@code HashMap}.
611      *
612      * @return a set view of the mappings contained in this map
613      */
614     // Set!(MapEntry!(K, V)) entrySet() {
615     //     Set!(MapEntry!(K, V)) es;
616     //     return (es = entrySet) is null ? (entrySet = new LinkedEntrySet()) : es;
617     // }
618 
619     // final class LinkedEntrySet : AbstractSet!(MapEntry!(K, V)) {
620     //     final int size()                 { return size; }
621     //     final void clear()               { this.outer.clear(); }
622     //     final Iterator!(MapEntry!(K, V)) iterator() {
623     //         return new LinkedEntryIterator();
624     //     }
625     //     // final bool contains(Object o) {
626     //     //     if (!(o instanceof MapEntry))
627     //     //         return false;
628     //     //     MapEntry<?,?> e = (MapEntry<?,?>) o;
629     //     //     Object key = e.getKey();
630     //     //     HashMapNode!(K, V) candidate = getNode(hash(key), key);
631     //     //     return candidate !is null && candidate.equals(e);
632     //     // }
633     //     // final bool remove(Object o) {
634     //     //     if (o instanceof MapEntry) {
635     //     //         MapEntry<?,?> e = (MapEntry<?,?>) o;
636     //     //         Object key = e.getKey();
637     //     //         Object value = e.getValue();
638     //     //         return removeNode(hash(key), key, value, true, true) !is null;
639     //     //     }
640     //     //     return false;
641     //     // }
642     //     // final Spliterator!(MapEntry!(K, V)) spliterator() {
643     //     //     return Spliterators.spliterator(this, Spliterator.SIZED |
644     //     //                                     Spliterator.ORDERED |
645     //     //                                     Spliterator.DISTINCT);
646     //     // }
647     //     // final void forEach(Consumer<MapEntry!(K, V)> action) {
648     //     //     if (action is null)
649     //     //         throw new NullPointerException();
650     //     //     int mc = modCount;
651     //     //     for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after)
652     //     //         action.accept(e);
653     //     //     if (modCount != mc)
654     //     //         throw new ConcurrentModificationException();
655     //     // }
656     // }
657 
658     // Map overrides
659 
660     // void replaceAll(BiFunction<K, V, ? : V> function) {
661     //     if (function is null)
662     //         throw new NullPointerException();
663     //     int mc = modCount;
664     //     for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after)
665     //         e.value = function.apply(e.key, e.value);
666     //     if (modCount != mc)
667     //         throw new ConcurrentModificationException();
668     // }
669 
670     // Iterators
671 
672     override int opApply(scope int delegate(ref K, ref V) dg)
673     {
674         if(dg is null)
675             throw new NullPointerException("");
676 
677         int result = 0;
678         int mc = modCount;
679         for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after)
680         {
681             result = dg(e.key, e.value);
682             if(result != 0) return result;
683         }
684 
685         if(modCount != mc)
686             throw new ConcurrentModificationException();
687 
688         return result;
689     }
690 
691     override int opApply(scope int delegate(MapEntry!(K, V) entry) dg)
692     {
693         if(dg is null)
694             throw new NullPointerException();
695 
696         int result = 0;
697         int mc = modCount;
698         for (LinkedHashMapEntry!(K, V) e = head; e !is null; e = e.after)
699         {
700             result = dg(e);
701             if(result != 0) return result;
702         }
703 
704         if(modCount != mc)
705             throw new ConcurrentModificationException();
706 
707         return result;
708     }
709 
710     override InputRange!K byKey()
711     {
712         return new KeyInputRange();
713     }
714 
715     override InputRange!V byValue()
716     {
717         return new ValueInputRange();
718     }
719 
720     mixin template LinkedHashMapIterator() {
721         private LinkedHashMapEntry!(K, V) next;
722         private LinkedHashMapEntry!(K, V) current;
723         private int expectedModCount;
724 
725         this() {
726             next = head;
727             expectedModCount = modCount;
728             current = null;
729         }
730 
731         final bool empty() {
732             return next is null;
733         }
734 
735         void popFront()
736         {
737             LinkedHashMapEntry!(K, V) e = next;
738             if (modCount != expectedModCount)
739                 throw new ConcurrentModificationException();
740             if (e is null)
741                 throw new NoSuchElementException();
742             current = e;
743             next = e.after;
744             // return e;
745         }
746     }
747 
748     final class KeyInputRange :  InputRange!K {
749         mixin LinkedHashMapIterator;
750 
751         final K front() @property { return next.key; }
752 
753         // https://forum.dlang.org/thread/amzthhonuozlobghqqgk@forum.dlang.org?page=1
754         // https://issues.dlang.org/show_bug.cgi?id=18036
755         final K moveFront() @property { throw new NotSupportedException(); }
756         
757         int opApply(scope int delegate(K) dg) {
758             if(dg is null)
759                 throw new NullPointerException();
760 
761             int result = 0;
762             int mc = modCount;
763             for (LinkedHashMapEntry!(K, V)e = head; e !is null; e = e.after)
764             {
765                 result = dg(e.key);
766                 if(result != 0) return result;
767             }
768 
769             if(modCount != mc)
770                 throw new ConcurrentModificationException();
771 
772             return result;
773         }
774 
775         int opApply(scope int delegate(size_t, K) dg) {
776             if(dg is null)
777                 throw new NullPointerException();
778 
779             int result = 0;
780             int mc = modCount;
781             size_t index = 0;
782             for (LinkedHashMapEntry!(K, V)e = head; e !is null; e = e.after)
783             {
784                 result = dg(index++, e.key);
785                 if(result != 0) return result;
786             }
787 
788             if(modCount != mc)
789                 throw new ConcurrentModificationException();
790 
791             return result;
792         }
793     }
794     
795     final class ValueInputRange :  InputRange!V {
796         mixin LinkedHashMapIterator;
797 
798         final V front() @property { return next.value; }
799 
800         final V moveFront() @property { throw new NotSupportedException(); }
801         
802         int opApply(scope int delegate(V) dg) {
803             if(dg is null)
804                 throw new NullPointerException();
805 
806             int result = 0;
807             int mc = modCount;
808             for (LinkedHashMapEntry!(K, V)e = head; e !is null; e = e.after)
809             {
810                 result = dg(e.value);
811                 if(result != 0) return result;
812             }
813 
814             if(modCount != mc)
815                 throw new ConcurrentModificationException();
816 
817             return result;
818         }
819 
820         int opApply(scope int delegate(size_t, V) dg) {
821             if(dg is null)
822                 throw new NullPointerException();
823 
824             int result = 0;
825             int mc = modCount;
826             size_t index = 0;
827             for (LinkedHashMapEntry!(K, V)e = head; e !is null; e = e.after) {
828                 result = dg(index++, e.value);
829                 if(result != 0) return result;
830             }
831 
832             if(modCount != mc)
833                 throw new ConcurrentModificationException();
834 
835             return result;
836         }
837     }
838 
839 
840 }
841