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 modulehunt.collection.LinkedHashMap;
13 14 importhunt.collection.AbstractCollection;
15 importhunt.collection.AbstractMap;
16 importhunt.collection.HashMap;
17 importhunt.collection.Map;
18 19 importhunt.Exceptions;
20 21 importstd.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 classLinkedHashMap(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 boolaccessOrder;
195 196 // internal utilities197 198 // link at the end of list199 privatevoidlinkNodeLast(LinkedHashMapEntry!(K, V) p) {
200 LinkedHashMapEntry!(K, V) last = tail;
201 tail = p;
202 if (lastisnull)
203 head = p;
204 else {
205 p.before = last;
206 last.after = p;
207 }
208 }
209 210 // apply src's links to dst211 privatevoidtransferLinks(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 (bisnull)
216 head = dst;
217 else218 b.after = dst;
219 if (aisnull)
220 tail = dst;
221 else222 a.before = dst;
223 }
224 225 // overrides of HashMap hook methods226 227 overridevoidreinitialize() {
228 super.reinitialize();
229 head = tail = null;
230 }
231 232 overrideHashMapNode!(K, V) newNode(size_thash, Kkey, Vvalue, HashMapNode!(K, V) e) {
233 LinkedHashMapEntry!(K, V) p = newLinkedHashMapEntry!(K, V)(hash, key, value, e);
234 linkNodeLast(p);
235 returnp;
236 }
237 238 overrideHashMapNode!(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 = newLinkedHashMapEntry!(K, V)(q.hash, q.key, q.value, next);
241 transferLinks(q, t);
242 returnt;
243 }
244 245 overrideTreeNode!(K, V) newTreeNode(size_thash, Kkey, Vvalue, HashMapNode!(K, V) next) {
246 TreeNode!(K, V) p = newTreeNode!(K, V)(hash, key, value, next);
247 linkNodeLast(p);
248 returnp;
249 }
250 251 overrideTreeNode!(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 = newTreeNode!(K, V)(q.hash, q.key, q.value, next);
254 transferLinks(q, t);
255 returnt;
256 }
257 258 overridevoidafterNodeRemoval(HashMapNode!(K, V) e) { // unlink259 LinkedHashMapEntry!(K, V) p =
260 cast(LinkedHashMapEntry!(K, V))e, b = p.before, a = p.after;
261 p.before = p.after = null;
262 if (bisnull)
263 head = a;
264 else265 b.after = a;
266 if (aisnull)
267 tail = b;
268 else269 a.before = b;
270 }
271 272 overridevoidafterNodeInsertion(boolevict) { // possibly remove eldest273 LinkedHashMapEntry!(K, V) first;
274 if (evict && (first = head) !isnull && removeEldestEntry(first)) {
275 Kkey = first.key;
276 removeNode(hash(key), key, V.init, false, true);
277 }
278 }
279 280 overridevoidafterNodeAccess(HashMapNode!(K, V) e) { // move node to last281 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 (bisnull)
287 head = a;
288 else289 b.after = a;
290 if (a !isnull)
291 a.before = b;
292 else293 last = b;
294 if (lastisnull)
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(intinitialCapacity, floatloadFactor) {
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(intinitialCapacity) {
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(intinitialCapacity,
374 floatloadFactor,
375 boolaccessOrder) {
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 overrideboolcontainsValue(Vvalue) {
390 for (LinkedHashMapEntry!(K, V) e = head; e !isnull; e = e.after) {
391 Vv = e.value;
392 if (v == value) // || (value !is null && value.equals(v))393 returntrue;
394 }
395 returnfalse;
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 overrideVget(Kkey) {
414 HashMapNode!(K, V) e = getNode(hash(key), key);
415 if (eisnull) {
416 staticif(is(V == class) || is(V == interface) || is(V == string)) {
417 returnnull;
418 } else {
419 thrownewNoSuchElementException();
420 }
421 }
422 423 if (accessOrder)
424 afterNodeAccess(e);
425 returne.value;
426 }
427 428 /**
429 * {@inheritDoc}
430 */431 overrideVgetOrDefault(Kkey, VdefaultValue) {
432 HashMapNode!(K, V) e;
433 if ((e = getNode(hash(key), key)) isnull)
434 returndefaultValue;
435 if (accessOrder)
436 afterNodeAccess(e);
437 returne.value;
438 }
439 440 /**
441 * {@inheritDoc}
442 */443 overridevoidclear() {
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() > 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 protectedboolremoveEldestEntry(MapEntry!(K, V) eldest) {
490 returnfalse;
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 overrides659 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 // Iterators671 672 overrideintopApply(scopeintdelegate(refK, refV) dg)
673 {
674 if(dgisnull)
675 thrownewNullPointerException("");
676 677 intresult = 0;
678 intmc = modCount;
679 for (LinkedHashMapEntry!(K, V) e = head; e !isnull; e = e.after)
680 {
681 result = dg(e.key, e.value);
682 if(result != 0) returnresult;
683 }
684 685 if(modCount != mc)
686 thrownewConcurrentModificationException();
687 688 returnresult;
689 }
690 691 overrideintopApply(scopeintdelegate(MapEntry!(K, V) entry) dg)
692 {
693 if(dgisnull)
694 thrownewNullPointerException();
695 696 intresult = 0;
697 intmc = modCount;
698 for (LinkedHashMapEntry!(K, V) e = head; e !isnull; e = e.after)
699 {
700 result = dg(e);
701 if(result != 0) returnresult;
702 }
703 704 if(modCount != mc)
705 thrownewConcurrentModificationException();
706 707 returnresult;
708 }
709 710 overrideInputRange!KbyKey()
711 {
712 returnnewKeyInputRange();
713 }
714 715 overrideInputRange!VbyValue()
716 {
717 returnnewValueInputRange();
718 }
719 720 mixintemplateLinkedHashMapIterator() {
721 privateLinkedHashMapEntry!(K, V) next;
722 privateLinkedHashMapEntry!(K, V) current;
723 privateintexpectedModCount;
724 725 this() {
726 next = head;
727 expectedModCount = modCount;
728 current = null;
729 }
730 731 finalboolempty() {
732 returnnextisnull;
733 }
734 735 voidpopFront()
736 {
737 LinkedHashMapEntry!(K, V) e = next;
738 if (modCount != expectedModCount)
739 thrownewConcurrentModificationException();
740 if (eisnull)
741 thrownewNoSuchElementException();
742 current = e;
743 next = e.after;
744 // return e;745 }
746 }
747 748 finalclassKeyInputRange : InputRange!K {
749 mixinLinkedHashMapIterator;
750 751 finalKfront() @property { returnnext.key; }
752 753 // https://forum.dlang.org/thread/amzthhonuozlobghqqgk@forum.dlang.org?page=1754 // https://issues.dlang.org/show_bug.cgi?id=18036755 finalKmoveFront() @property { thrownewNotSupportedException(); }
756 757 intopApply(scopeintdelegate(K) dg) {
758 if(dgisnull)
759 thrownewNullPointerException();
760 761 intresult = 0;
762 intmc = modCount;
763 for (LinkedHashMapEntry!(K, V)e = head; e !isnull; e = e.after)
764 {
765 result = dg(e.key);
766 if(result != 0) returnresult;
767 }
768 769 if(modCount != mc)
770 thrownewConcurrentModificationException();
771 772 returnresult;
773 }
774 775 intopApply(scopeintdelegate(size_t, K) dg) {
776 if(dgisnull)
777 thrownewNullPointerException();
778 779 intresult = 0;
780 intmc = modCount;
781 size_tindex = 0;
782 for (LinkedHashMapEntry!(K, V)e = head; e !isnull; e = e.after)
783 {
784 result = dg(index++, e.key);
785 if(result != 0) returnresult;
786 }
787 788 if(modCount != mc)
789 thrownewConcurrentModificationException();
790 791 returnresult;
792 }
793 }
794 795 finalclassValueInputRange : InputRange!V {
796 mixinLinkedHashMapIterator;
797 798 finalVfront() @property { returnnext.value; }
799 800 finalVmoveFront() @property { thrownewNotSupportedException(); }
801 802 intopApply(scopeintdelegate(V) dg) {
803 if(dgisnull)
804 thrownewNullPointerException();
805 806 intresult = 0;
807 intmc = modCount;
808 for (LinkedHashMapEntry!(K, V)e = head; e !isnull; e = e.after)
809 {
810 result = dg(e.value);
811 if(result != 0) returnresult;
812 }
813 814 if(modCount != mc)
815 thrownewConcurrentModificationException();
816 817 returnresult;
818 }
819 820 intopApply(scopeintdelegate(size_t, V) dg) {
821 if(dgisnull)
822 thrownewNullPointerException();
823 824 intresult = 0;
825 intmc = modCount;
826 size_tindex = 0;
827 for (LinkedHashMapEntry!(K, V)e = head; e !isnull; e = e.after) {
828 result = dg(index++, e.value);
829 if(result != 0) returnresult;
830 }
831 832 if(modCount != mc)
833 thrownewConcurrentModificationException();
834 835 returnresult;
836 }
837 }
838 839 840 }
841