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