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