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.Map; 13 14 import hunt.collection.Collection; 15 import hunt.collection.Set; 16 import hunt.util.Common; 17 import hunt.Functions; 18 import hunt.Object; 19 20 import std.range.interfaces : InputRange; 21 22 /** 23 */ 24 interface Map(K,V) : Iterable!(K,V), IObject { 25 // Query Operations 26 27 /** 28 * Returns the number of key-value mappings in this map. If the 29 * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns 30 * <tt>Integer.MAX_VALUE</tt>. 31 * 32 * @return the number of key-value mappings in this map 33 */ 34 int size(); 35 36 /** 37 * Returns <tt>true</tt> if this map contains no key-value mappings. 38 * 39 * @return <tt>true</tt> if this map contains no key-value mappings 40 */ 41 bool isEmpty(); 42 43 /** 44 * Returns <tt>true</tt> if this map contains a mapping for the specified 45 * key. More formally, returns <tt>true</tt> if and only if 46 * this map contains a mapping for a key <tt>k</tt> such that 47 * <tt>(key==null ? k==null : key.equals(k))</tt>. (There can be 48 * at most one such mapping.) 49 * 50 * @param key key whose presence in this map is to be tested 51 * @return <tt>true</tt> if this map contains a mapping for the specified 52 * key 53 * @throws ClassCastException if the key is of an inappropriate type for 54 * this map 55 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 56 * @throws NullPointerException if the specified key is null and this map 57 * does not permit null keys 58 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 59 */ 60 bool containsKey(K key); 61 62 /** 63 * Returns <tt>true</tt> if this map maps one or more keys to the 64 * specified value. More formally, returns <tt>true</tt> if and only if 65 * this map contains at least one mapping to a value <tt>v</tt> such that 66 * <tt>(value==null ? v==null : value.equals(v))</tt>. This operation 67 * will probably require time linear in the map size for most 68 * implementations of the <tt>Map</tt> interface. 69 * 70 * @param value value whose presence in this map is to be tested 71 * @return <tt>true</tt> if this map maps one or more keys to the 72 * specified value 73 * @throws ClassCastException if the value is of an inappropriate type for 74 * this map 75 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 76 * @throws NullPointerException if the specified value is null and this 77 * map does not permit null values 78 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 79 */ 80 bool containsValue(V value); 81 82 /** 83 * Returns the value to which the specified key is mapped, 84 * or {@code null} if this map contains no mapping for the key. 85 * 86 * <p>More formally, if this map contains a mapping from a key 87 * {@code k} to a value {@code v} such that {@code (key==null ? k==null : 88 * key.equals(k))}, then this method returns {@code v}; otherwise 89 * it returns {@code null}. (There can be at most one such mapping.) 90 * 91 * <p>If this map permits null values, then a return value of 92 * {@code null} does not <i>necessarily</i> indicate that the map 93 * contains no mapping for the key; it's also possible that the map 94 * explicitly maps the key to {@code null}. The {@link #containsKey 95 * containsKey} operation may be used to distinguish these two cases. 96 * 97 * @param key the key whose associated value is to be returned 98 * @return the value to which the specified key is mapped, or 99 * {@code null} if this map contains no mapping for the key 100 * @throws ClassCastException if the key is of an inappropriate type for 101 * this map 102 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 103 * @throws NullPointerException if the specified key is null and this map 104 * does not permit null keys 105 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 106 */ 107 V get(K key); 108 109 V opIndex(K key); 110 111 // Modification Operations 112 113 /** 114 * Associates the specified value with the specified key in this map 115 * (optional operation). If the map previously contained a mapping for 116 * the key, the old value is replaced by the specified value. (A map 117 * <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only 118 * if {@link #containsKey(Object) m.containsKey(k)} would return 119 * <tt>true</tt>.) 120 * 121 * @param key key with which the specified value is to be associated 122 * @param value value to be associated with the specified key 123 * @return the previous value associated with <tt>key</tt>, or 124 * <tt>null</tt> if there was no mapping for <tt>key</tt>. 125 * (A <tt>null</tt> return can also indicate that the map 126 * previously associated <tt>null</tt> with <tt>key</tt>, 127 * if the implementation supports <tt>null</tt> values.) 128 * @throws UnsupportedOperationException if the <tt>put</tt> operation 129 * is not supported by this map 130 * @throws ClassCastException if the class of the specified key or value 131 * prevents it from being stored in this map 132 * @throws NullPointerException if the specified key or value is null 133 * and this map does not permit null keys or values 134 * @throws IllegalArgumentException if some property of the specified key 135 * or value prevents it from being stored in this map 136 */ 137 V put(K key, V value); 138 139 /** 140 * Removes the mapping for a key from this map if it is present 141 * (optional operation). More formally, if this map contains a mapping 142 * from key <tt>k</tt> to value <tt>v</tt> such that 143 * <code>(key==null ? k==null : key.equals(k))</code>, that mapping 144 * is removed. (The map can contain at most one such mapping.) 145 * 146 * <p>Returns the value to which this map previously associated the key, 147 * or <tt>null</tt> if the map contained no mapping for the key. 148 * 149 * <p>If this map permits null values, then a return value of 150 * <tt>null</tt> does not <i>necessarily</i> indicate that the map 151 * contained no mapping for the key; it's also possible that the map 152 * explicitly mapped the key to <tt>null</tt>. 153 * 154 * <p>The map will not contain a mapping for the specified key once the 155 * call returns. 156 * 157 * @param key key whose mapping is to be removed from the map 158 * @return the previous value associated with <tt>key</tt>, or 159 * <tt>null</tt> if there was no mapping for <tt>key</tt>. 160 * @throws UnsupportedOperationException if the <tt>remove</tt> operation 161 * is not supported by this map 162 * @throws ClassCastException if the key is of an inappropriate type for 163 * this map 164 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 165 * @throws NullPointerException if the specified key is null and this 166 * map does not permit null keys 167 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 168 */ 169 V remove(K key); 170 171 172 // Bulk Operations 173 174 /** 175 * Copies all of the mappings from the specified map to this map 176 * (optional operation). The effect of this call is equivalent to that 177 * of calling {@link #put(Object,Object) put(k, v)} on this map once 178 * for each mapping from key <tt>k</tt> to value <tt>v</tt> in the 179 * specified map. The behavior of this operation is undefined if the 180 * specified map is modified while the operation is in progress. 181 * 182 * @param m mappings to be stored in this map 183 * @throws UnsupportedOperationException if the <tt>putAll</tt> operation 184 * is not supported by this map 185 * @throws ClassCastException if the class of a key or value in the 186 * specified map prevents it from being stored in this map 187 * @throws NullPointerException if the specified map is null, or if 188 * this map does not permit null keys or values, and the 189 * specified map contains null keys or values 190 * @throws IllegalArgumentException if some property of a key or value in 191 * the specified map prevents it from being stored in this map 192 */ 193 void putAll(Map!(K, V) m); 194 195 /** 196 * Removes all of the mappings from this map (optional operation). 197 * The map will be empty after this call returns. 198 * 199 * @throws UnsupportedOperationException if the <tt>clear</tt> operation 200 * is not supported by this map 201 */ 202 void clear(); 203 204 // Views 205 206 /** 207 * Returns a {@link Collection} view of the values contained in this map. 208 * The collection is backed by the map, so changes to the map are 209 * reflected in the collection, and vice-versa. If the map is 210 * modified while an iteration over the collection is in progress 211 * (except through the iterator's own <tt>remove</tt> operation), 212 * the results of the iteration are undefined. The collection 213 * supports element removal, which removes the corresponding 214 * mapping from the map, via the <tt>Iterator.remove</tt>, 215 * <tt>Collection.remove</tt>, <tt>removeAll</tt>, 216 * <tt>retainAll</tt> and <tt>clear</tt> operations. It does not 217 * support the <tt>add</tt> or <tt>addAll</tt> operations. 218 * 219 * @return a collection view of the values contained in this map 220 */ 221 // Collection!V values(); 222 V[] values(); 223 224 string toString(); 225 226 227 // Comparison and hashing 228 229 /** 230 * Compares the specified object with this map for equality. Returns 231 * <tt>true</tt> if the given object is also a map and the two maps 232 * represent the same mappings. More formally, two maps <tt>m1</tt> and 233 * <tt>m2</tt> represent the same mappings if 234 * <tt>m1.entrySet().equals(m2.entrySet())</tt>. This ensures that the 235 * <tt>equals</tt> method works properly across different implementations 236 * of the <tt>Map</tt> interface. 237 * 238 * @param o object to be compared for equality with this map 239 * @return <tt>true</tt> if the specified object is equal to this map 240 */ 241 // bool opEquals(Object o); 242 243 /** 244 * Returns the hash code value for this map. The hash code of a map is 245 * defined to be the sum of the hash codes of each entry in the map's 246 * <tt>entrySet()</tt> view. This ensures that <tt>m1.equals(m2)</tt> 247 * implies that <tt>m1.toHash()==m2.toHash()</tt> for any two maps 248 * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of 249 * {@link Object#toHash}. 250 * 251 * @return the hash code value for this map 252 * @see MapEntry#toHash() 253 * @see Object#equals(Object) 254 * @see #equals(Object) 255 */ 256 size_t toHash() @trusted nothrow; 257 258 // Defaultable methods 259 260 /** 261 * Returns the value to which the specified key is mapped, or 262 * {@code defaultValue} if this map contains no mapping for the key. 263 * 264 * @implSpec 265 * The final implementation makes no guarantees about synchronization 266 * or atomicity properties of this method. Any implementation providing 267 * atomicity guarantees must override this method and document its 268 * concurrency properties. 269 * 270 * @param key the key whose associated value is to be returned 271 * @param defaultValue the final mapping of the key 272 * @return the value to which the specified key is mapped, or 273 * {@code defaultValue} if this map contains no mapping for the key 274 * @throws ClassCastException if the key is of an inappropriate type for 275 * this map 276 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 277 * @throws NullPointerException if the specified key is null and this map 278 * does not permit null keys 279 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 280 * @since 1.8 281 */ 282 // final V getOrDefault(K key, V defaultValue) { 283 // V v; 284 // static if(is(V == class)) 285 // { 286 // return (((v = get(key)) !is null) || containsKey(key)) 287 // ? v 288 // : defaultValue; 289 // } 290 // else 291 // { 292 // return containsKey(key) ? v : defaultValue; 293 // } 294 // } 295 296 /** 297 * Performs the given action for each entry in this map until all entries 298 * have been processed or the action throws an exception. Unless 299 * otherwise specified by the implementing class, actions are performed in 300 * the order of entry set iteration (if an iteration order is specified.) 301 * Exceptions thrown by the action are relayed to the caller. 302 * 303 * @implSpec 304 * The final implementation is equivalent to, for this {@code map}: 305 * <pre> {@code 306 * for (MapEntry<K, V> entry : map.entrySet()) 307 * action.accept(entry.getKey(), entry.getValue()); 308 * }</pre> 309 * 310 * The final implementation makes no guarantees about synchronization 311 * or atomicity properties of this method. Any implementation providing 312 * atomicity guarantees must override this method and document its 313 * concurrency properties. 314 * 315 * @param action The action to be performed for each entry 316 * @throws NullPointerException if the specified action is null 317 * @throws ConcurrentModificationException if an entry is found to be 318 * removed during iteration 319 * @since 1.8 320 */ 321 int opApply(scope int delegate(ref K, ref V) dg); 322 323 /// ditto 324 int opApply(scope int delegate(MapEntry!(K, V) entry) dg); 325 326 InputRange!K byKey(); 327 328 InputRange!V byValue(); 329 330 /** 331 * Replaces each entry's value with the result of invoking the given 332 * function on that entry until all entries have been processed or the 333 * function throws an exception. Exceptions thrown by the function are 334 * relayed to the caller. 335 * 336 * @implSpec 337 * <p>The final implementation is equivalent to, for this {@code map}: 338 * <pre> {@code 339 * for (MapEntry<K, V> entry : map.entrySet()) 340 * entry.setValue(function.apply(entry.getKey(), entry.getValue())); 341 * }</pre> 342 * 343 * <p>The final implementation makes no guarantees about synchronization 344 * or atomicity properties of this method. Any implementation providing 345 * atomicity guarantees must override this method and document its 346 * concurrency properties. 347 * 348 * @param function the function to apply to each entry 349 * @throws UnsupportedOperationException if the {@code set} operation 350 * is not supported by this map's entry set iterator. 351 * @throws ClassCastException if the class of a replacement value 352 * prevents it from being stored in this map 353 * @throws NullPointerException if the specified function is null, or the 354 * specified replacement value is null, and this map does not permit null 355 * values 356 * @throws ClassCastException if a replacement value is of an inappropriate 357 * type for this map 358 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 359 * @throws NullPointerException if function or a replacement value is null, 360 * and this map does not permit null keys or values 361 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 362 * @throws IllegalArgumentException if some property of a replacement value 363 * prevents it from being stored in this map 364 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 365 * @throws ConcurrentModificationException if an entry is found to be 366 * removed during iteration 367 * @since 1.8 368 */ 369 // final void replaceAll(BiFunction<K, V, V> function) { 370 // Objects.requireNonNull(function); 371 // for (MapEntry<K, V> entry : entrySet()) { 372 // K k; 373 // V v; 374 // try { 375 // k = entry.getKey(); 376 // v = entry.getValue(); 377 // } catch(IllegalStateException ise) { 378 // // this usually means the entry is no longer in the map. 379 // throw new ConcurrentModificationException(ise); 380 // } 381 382 // // ise thrown from function is not a cme. 383 // v = function.apply(k, v); 384 385 // try { 386 // entry.setValue(v); 387 // } catch(IllegalStateException ise) { 388 // // this usually means the entry is no longer in the map. 389 // throw new ConcurrentModificationException(ise); 390 // } 391 // } 392 // } 393 394 /** 395 * If the specified key is not already associated with a value (or is mapped 396 * to {@code null}) associates it with the given value and returns 397 * {@code null}, else returns the current value. 398 * 399 * @implSpec 400 * The final implementation is equivalent to, for this {@code 401 * map}: 402 * 403 * <pre> {@code 404 * V v = map.get(key); 405 * if (v is null) 406 * v = map.put(key, value); 407 * 408 * return v; 409 * }</pre> 410 * 411 * <p>The final implementation makes no guarantees about synchronization 412 * or atomicity properties of this method. Any implementation providing 413 * atomicity guarantees must override this method and document its 414 * concurrency properties. 415 * 416 * @param key key with which the specified value is to be associated 417 * @param value value to be associated with the specified key 418 * @return the previous value associated with the specified key, or 419 * {@code null} if there was no mapping for the key. 420 * (A {@code null} return can also indicate that the map 421 * previously associated {@code null} with the key, 422 * if the implementation supports null values.) 423 * @throws UnsupportedOperationException if the {@code put} operation 424 * is not supported by this map 425 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 426 * @throws ClassCastException if the key or value is of an inappropriate 427 * type for this map 428 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 429 * @throws NullPointerException if the specified key or value is null, 430 * and this map does not permit null keys or values 431 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 432 * @throws IllegalArgumentException if some property of the specified key 433 * or value prevents it from being stored in this map 434 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 435 * @since 1.8 436 */ 437 V putIfAbsent(K key, V value); 438 439 /** 440 * Removes the entry for the specified key only if it is currently 441 * mapped to the specified value. 442 * 443 * @implSpec 444 * The final implementation is equivalent to, for this {@code map}: 445 * 446 * <pre> {@code 447 * if (map.containsKey(key) && Objects.equals(map.get(key), value)) { 448 * map.remove(key); 449 * return true; 450 * } else 451 * return false; 452 * }</pre> 453 * 454 * <p>The final implementation makes no guarantees about synchronization 455 * or atomicity properties of this method. Any implementation providing 456 * atomicity guarantees must override this method and document its 457 * concurrency properties. 458 * 459 * @param key key with which the specified value is associated 460 * @param value value expected to be associated with the specified key 461 * @return {@code true} if the value was removed 462 * @throws UnsupportedOperationException if the {@code remove} operation 463 * is not supported by this map 464 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 465 * @throws ClassCastException if the key or value is of an inappropriate 466 * type for this map 467 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 468 * @throws NullPointerException if the specified key or value is null, 469 * and this map does not permit null keys or values 470 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 471 * @since 1.8 472 */ 473 bool remove(K key, V value); 474 475 476 /** 477 * Replaces the entry for the specified key only if currently 478 * mapped to the specified value. 479 * 480 * @implSpec 481 * The final implementation is equivalent to, for this {@code map}: 482 * 483 * <pre> {@code 484 * if (map.containsKey(key) && Objects.equals(map.get(key), value)) { 485 * map.put(key, newValue); 486 * return true; 487 * } else 488 * return false; 489 * }</pre> 490 * 491 * The final implementation does not throw NullPointerException 492 * for maps that do not support null values if oldValue is null unless 493 * newValue is also null. 494 * 495 * <p>The final implementation makes no guarantees about synchronization 496 * or atomicity properties of this method. Any implementation providing 497 * atomicity guarantees must override this method and document its 498 * concurrency properties. 499 * 500 * @param key key with which the specified value is associated 501 * @param oldValue value expected to be associated with the specified key 502 * @param newValue value to be associated with the specified key 503 * @return {@code true} if the value was replaced 504 * @throws UnsupportedOperationException if the {@code put} operation 505 * is not supported by this map 506 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 507 * @throws ClassCastException if the class of a specified key or value 508 * prevents it from being stored in this map 509 * @throws NullPointerException if a specified key or newValue is null, 510 * and this map does not permit null keys or values 511 * @throws NullPointerException if oldValue is null and this map does not 512 * permit null values 513 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 514 * @throws IllegalArgumentException if some property of a specified key 515 * or value prevents it from being stored in this map 516 * @since 1.8 517 */ 518 bool replace(K key, V oldValue, V newValue); 519 520 /** 521 * Replaces the entry for the specified key only if it is 522 * currently mapped to some value. 523 * 524 * @implSpec 525 * The final implementation is equivalent to, for this {@code map}: 526 * 527 * <pre> {@code 528 * if (map.containsKey(key)) { 529 * return map.put(key, value); 530 * } else 531 * return null; 532 * }</pre> 533 * 534 * <p>The final implementation makes no guarantees about synchronization 535 * or atomicity properties of this method. Any implementation providing 536 * atomicity guarantees must override this method and document its 537 * concurrency properties. 538 * 539 * @param key key with which the specified value is associated 540 * @param value value to be associated with the specified key 541 * @return the previous value associated with the specified key, or 542 * {@code null} if there was no mapping for the key. 543 * (A {@code null} return can also indicate that the map 544 * previously associated {@code null} with the key, 545 * if the implementation supports null values.) 546 * @throws UnsupportedOperationException if the {@code put} operation 547 * is not supported by this map 548 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 549 * @throws ClassCastException if the class of the specified key or value 550 * prevents it from being stored in this map 551 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 552 * @throws NullPointerException if the specified key or value is null, 553 * and this map does not permit null keys or values 554 * @throws IllegalArgumentException if some property of the specified key 555 * or value prevents it from being stored in this map 556 * @since 1.8 557 */ 558 V replace(K key, V value); 559 560 /** 561 * If the specified key is not already associated with a value (or is mapped 562 * to {@code null}), attempts to compute its value using the given mapping 563 * function and enters it into this map unless {@code null}. 564 * 565 * <p>If the function returns {@code null} no mapping is recorded. If 566 * the function itself throws an (unchecked) exception, the 567 * exception is rethrown, and no mapping is recorded. The most 568 * common usage is to construct a new object serving as an initial 569 * mapped value or memoized result, as in: 570 * 571 * <pre> {@code 572 * map.computeIfAbsent(key, k -> new Value(f(k))); 573 * }</pre> 574 * 575 * <p>Or to implement a multi-value map, {@code Map<K,Collection!V>}, 576 * supporting multiple values per key: 577 * 578 * <pre> {@code 579 * map.computeIfAbsent(key, k -> new HashSet!V()).add(v); 580 * }</pre> 581 * 582 * 583 * @implSpec 584 * The final implementation is equivalent to the following steps for this 585 * {@code map}, then returning the current value or {@code null} if now 586 * absent: 587 * 588 * <pre> {@code 589 * if (map.get(key) is null) { 590 * V newValue = mappingFunction.apply(key); 591 * if (newValue !is null) 592 * map.put(key, newValue); 593 * } 594 * }</pre> 595 * 596 * <p>The final implementation makes no guarantees about synchronization 597 * or atomicity properties of this method. Any implementation providing 598 * atomicity guarantees must override this method and document its 599 * concurrency properties. In particular, all implementations of 600 * subinterface {@link hunt.concurrency.ConcurrentMap} must document 601 * whether the function is applied once atomically only if the value is not 602 * present. 603 * 604 * @param key key with which the specified value is to be associated 605 * @param mappingFunction the function to compute a value 606 * @return the current (existing or computed) value associated with 607 * the specified key, or null if the computed value is null 608 * @throws NullPointerException if the specified key is null and 609 * this map does not support null keys, or the mappingFunction 610 * is null 611 * @throws UnsupportedOperationException if the {@code put} operation 612 * is not supported by this map 613 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 614 * @throws ClassCastException if the class of the specified key or value 615 * prevents it from being stored in this map 616 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 617 * @since 1.8 618 */ 619 620 final V computeIfAbsent(K key, Function!(K, V) mappingFunction) { 621 assert(mappingFunction !is null); 622 623 if (containsKey(key)) { 624 return get(key); 625 } 626 else { 627 V newValue = mappingFunction(key); 628 static if(is(V == class)) 629 { 630 if (newValue !is null) { 631 put(key, newValue); 632 return newValue; 633 } 634 else 635 return V.init; 636 } 637 else 638 { 639 put(key, newValue); 640 return newValue; 641 } 642 } 643 } 644 645 646 /** 647 * If the value for the specified key is present and non-null, attempts to 648 * compute a new mapping given the key and its current mapped value. 649 * 650 * <p>If the function returns {@code null}, the mapping is removed. If the 651 * function itself throws an (unchecked) exception, the exception is 652 * rethrown, and the current mapping is left unchanged. 653 * 654 * @implSpec 655 * The final implementation is equivalent to performing the following 656 * steps for this {@code map}, then returning the current value or 657 * {@code null} if now absent: 658 * 659 * <pre> {@code 660 * if (map.get(key) !is null) { 661 * V oldValue = map.get(key); 662 * V newValue = remappingFunction.apply(key, oldValue); 663 * if (newValue !is null) 664 * map.put(key, newValue); 665 * else 666 * map.remove(key); 667 * } 668 * }</pre> 669 * 670 * <p>The final implementation makes no guarantees about synchronization 671 * or atomicity properties of this method. Any implementation providing 672 * atomicity guarantees must override this method and document its 673 * concurrency properties. In particular, all implementations of 674 * subinterface {@link hunt.concurrency.ConcurrentMap} must document 675 * whether the function is applied once atomically only if the value is not 676 * present. 677 * 678 * @param key key with which the specified value is to be associated 679 * @param remappingFunction the function to compute a value 680 * @return the new value associated with the specified key, or null if none 681 * @throws NullPointerException if the specified key is null and 682 * this map does not support null keys, or the 683 * remappingFunction is null 684 * @throws UnsupportedOperationException if the {@code put} operation 685 * is not supported by this map 686 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 687 * @throws ClassCastException if the class of the specified key or value 688 * prevents it from being stored in this map 689 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 690 * @since 1.8 691 */ 692 // final V computeIfPresent(K key, 693 // BiFunction<K, V, V> remappingFunction) { 694 // Objects.requireNonNull(remappingFunction); 695 // V oldValue; 696 // if ((oldValue = get(key)) !is null) { 697 // V newValue = remappingFunction.apply(key, oldValue); 698 // if (newValue !is null) { 699 // put(key, newValue); 700 // return newValue; 701 // } else { 702 // remove(key); 703 // return null; 704 // } 705 // } else { 706 // return null; 707 // } 708 // } 709 710 /** 711 * Attempts to compute a mapping for the specified key and its current 712 * mapped value (or {@code null} if there is no current mapping). For 713 * example, to either create or append a {@code string} msg to a value 714 * mapping: 715 * 716 * <pre> {@code 717 * map.compute(key, (k, v) -> (v is null) ? msg : v.concat(msg))}</pre> 718 * (Method {@link #merge merge()} is often simpler to use for such purposes.) 719 * 720 * <p>If the function returns {@code null}, the mapping is removed (or 721 * remains absent if initially absent). If the function itself throws an 722 * (unchecked) exception, the exception is rethrown, and the current mapping 723 * is left unchanged. 724 * 725 * @implSpec 726 * The final implementation is equivalent to performing the following 727 * steps for this {@code map}, then returning the current value or 728 * {@code null} if absent: 729 * 730 * <pre> {@code 731 * V oldValue = map.get(key); 732 * V newValue = remappingFunction.apply(key, oldValue); 733 * if (oldValue !is null ) { 734 * if (newValue !is null) 735 * map.put(key, newValue); 736 * else 737 * map.remove(key); 738 * } else { 739 * if (newValue !is null) 740 * map.put(key, newValue); 741 * else 742 * return null; 743 * } 744 * }</pre> 745 * 746 * <p>The final implementation makes no guarantees about synchronization 747 * or atomicity properties of this method. Any implementation providing 748 * atomicity guarantees must override this method and document its 749 * concurrency properties. In particular, all implementations of 750 * subinterface {@link hunt.concurrency.ConcurrentMap} must document 751 * whether the function is applied once atomically only if the value is not 752 * present. 753 * 754 * @param key key with which the specified value is to be associated 755 * @param remappingFunction the function to compute a value 756 * @return the new value associated with the specified key, or null if none 757 * @throws NullPointerException if the specified key is null and 758 * this map does not support null keys, or the 759 * remappingFunction is null 760 * @throws UnsupportedOperationException if the {@code put} operation 761 * is not supported by this map 762 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 763 * @throws ClassCastException if the class of the specified key or value 764 * prevents it from being stored in this map 765 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 766 * @since 1.8 767 */ 768 // final V compute(K key, 769 // BiFunction<K, V, V> remappingFunction) { 770 // Objects.requireNonNull(remappingFunction); 771 // V oldValue = get(key); 772 773 // V newValue = remappingFunction.apply(key, oldValue); 774 // if (newValue is null) { 775 // // delete mapping 776 // if (oldValue !is null || containsKey(key)) { 777 // // something to remove 778 // remove(key); 779 // return null; 780 // } else { 781 // // nothing to do. Leave things as they were. 782 // return null; 783 // } 784 // } else { 785 // // add or replace old mapping 786 // put(key, newValue); 787 // return newValue; 788 // } 789 // } 790 791 /** 792 * If the specified key is not already associated with a value or is 793 * associated with null, associates it with the given non-null value. 794 * Otherwise, replaces the associated value with the results of the given 795 * remapping function, or removes if the result is {@code null}. This 796 * method may be of use when combining multiple mapped values for a key. 797 * For example, to either create or append a {@code string msg} to a 798 * value mapping: 799 * 800 * <pre> {@code 801 * map.merge(key, msg, string::concat) 802 * }</pre> 803 * 804 * <p>If the function returns {@code null} the mapping is removed. If the 805 * function itself throws an (unchecked) exception, the exception is 806 * rethrown, and the current mapping is left unchanged. 807 * 808 * @implSpec 809 * The final implementation is equivalent to performing the following 810 * steps for this {@code map}, then returning the current value or 811 * {@code null} if absent: 812 * 813 * <pre> {@code 814 * V oldValue = map.get(key); 815 * V newValue = (oldValue is null) ? value : 816 * remappingFunction.apply(oldValue, value); 817 * if (newValue is null) 818 * map.remove(key); 819 * else 820 * map.put(key, newValue); 821 * }</pre> 822 * 823 * <p>The final implementation makes no guarantees about synchronization 824 * or atomicity properties of this method. Any implementation providing 825 * atomicity guarantees must override this method and document its 826 * concurrency properties. In particular, all implementations of 827 * subinterface {@link hunt.concurrency.ConcurrentMap} must document 828 * whether the function is applied once atomically only if the value is not 829 * present. 830 * 831 * @param key key with which the resulting value is to be associated 832 * @param value the non-null value to be merged with the existing value 833 * associated with the key or, if no existing value or a null value 834 * is associated with the key, to be associated with the key 835 * @param remappingFunction the function to recompute a value if present 836 * @return the new value associated with the specified key, or null if no 837 * value is associated with the key 838 * @throws UnsupportedOperationException if the {@code put} operation 839 * is not supported by this map 840 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 841 * @throws ClassCastException if the class of the specified key or value 842 * prevents it from being stored in this map 843 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>) 844 * @throws NullPointerException if the specified key is null and this map 845 * does not support null keys or the value or remappingFunction is 846 * null 847 * @since 1.8 848 */ 849 // final V merge(K key, V value, 850 // BiFunction<V, V, V> remappingFunction) { 851 // Objects.requireNonNull(remappingFunction); 852 // Objects.requireNonNull(value); 853 // V oldValue = get(key); 854 // V newValue = (oldValue is null) ? value : 855 // remappingFunction.apply(oldValue, value); 856 // if(newValue is null) { 857 // remove(key); 858 // } else { 859 // put(key, newValue); 860 // } 861 // return newValue; 862 // } 863 } 864 865 866 /* A map entry (key-value pair). The <tt>Map.entrySet</tt> method returns 867 * a collection-view of the map, whose elements are of this class. The 868 * <i>only</i> way to obtain a reference to a map entry is from the 869 * iterator of this collection-view. These <tt>MapEntry</tt> objects are 870 * valid <i>only</i> for the duration of the iteration; more formally, 871 * the behavior of a map entry is undefined if the backing map has been 872 * modified after the entry was returned by the iterator, except through 873 * the <tt>setValue</tt> operation on the map entry. 874 * 875 * @see Map#entrySet() 876 * @since 1.2 877 */ 878 interface MapEntry(K,V) : IObject { 879 /** 880 * Returns the key corresponding to this entry. 881 * 882 * @return the key corresponding to this entry 883 * @throws IllegalStateException implementations may, but are not 884 * required to, throw this exception if the entry has been 885 * removed from the backing map. 886 */ 887 K getKey(); 888 889 /** 890 * Returns the value corresponding to this entry. If the mapping 891 * has been removed from the backing map (by the iterator's 892 * <tt>remove</tt> operation), the results of this call are undefined. 893 * 894 * @return the value corresponding to this entry 895 * @throws IllegalStateException implementations may, but are not 896 * required to, throw this exception if the entry has been 897 * removed from the backing map. 898 */ 899 V getValue(); 900 901 /** 902 * Replaces the value corresponding to this entry with the specified 903 * value (optional operation). (Writes through to the map.) The 904 * behavior of this call is undefined if the mapping has already been 905 * removed from the map (by the iterator's <tt>remove</tt> operation). 906 * 907 * @param value new value to be stored in this entry 908 * @return old value corresponding to the entry 909 * @throws UnsupportedOperationException if the <tt>put</tt> operation 910 * is not supported by the backing map 911 * @throws ClassCastException if the class of the specified value 912 * prevents it from being stored in the backing map 913 * @throws NullPointerException if the backing map does not permit 914 * null values, and the specified value is null 915 * @throws IllegalArgumentException if some property of this value 916 * prevents it from being stored in the backing map 917 * @throws IllegalStateException implementations may, but are not 918 * required to, throw this exception if the entry has been 919 * removed from the backing map. 920 */ 921 V setValue(V value); 922 923 /** 924 * Compares the specified object with this entry for equality. 925 * Returns <tt>true</tt> if the given object is also a map entry and 926 * the two entries represent the same mapping. More formally, two 927 * entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping 928 * if<pre> 929 * (e1.getKey()==null ? 930 * e2.getKey()==null : e1.getKey().equals(e2.getKey())) && 931 * (e1.getValue()==null ? 932 * e2.getValue()==null : e1.getValue().equals(e2.getValue())) 933 * </pre> 934 * This ensures that the <tt>equals</tt> method works properly across 935 * different implementations of the <tt>MapEntry</tt> interface. 936 * 937 * @param o object to be compared for equality with this map entry 938 * @return <tt>true</tt> if the specified object is equal to this map 939 * entry 940 */ 941 // bool opEquals(MapEntry!(K,V) o); 942 943 /** 944 * Returns the hash code value for this map entry. The hash code 945 * of a map entry <tt>e</tt> is defined to be: <pre> 946 * (e.getKey()==null ? 0 : e.getKey().hashCode()) ^ 947 * (e.getValue()==null ? 0 : e.getValue().hashCode()) 948 * </pre> 949 * This ensures that <tt>e1.equals(e2)</tt> implies that 950 * <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries 951 * <tt>e1</tt> and <tt>e2</tt>, as required by the general 952 * contract of <tt>Object.hashCode</tt>. 953 * 954 * @return the hash code value for this map entry 955 * @see Object#hashCode() 956 * @see Object#equals(Object) 957 * @see #equals(Object) 958 */ 959 // size_t toHash() @trusted nothrow; 960 961 /** 962 * Returns a comparator that compares {@link MapEntry} in natural order on key. 963 * 964 * <p>The returned comparator is serializable and throws {@link 965 * NullPointerException} when comparing an entry with a null key. 966 * 967 * @param !K the {@link Comparable} type of then map keys 968 * @param !V the type of the map values 969 * @return a comparator that compares {@link MapEntry} in natural order on key. 970 * @see Comparable 971 * @since 1.8 972 */ 973 // public static <K extends Comparable<K>, V> Comparator<MapEntry!(K,V)> comparingByKey() { 974 // return (Comparator<MapEntry<K, V>> & Serializable) 975 // (c1, c2) -> c1.getKey().compareTo(c2.getKey()); 976 // } 977 978 /** 979 * Returns a comparator that compares {@link MapEntry} in natural order on value. 980 * 981 * <p>The returned comparator is serializable and throws {@link 982 * NullPointerException} when comparing an entry with null values. 983 * 984 * @param !K the type of the map keys 985 * @param !V the {@link Comparable} type of the map values 986 * @return a comparator that compares {@link MapEntry} in natural order on value. 987 * @see Comparable 988 * @since 1.8 989 */ 990 // public static <K, V extends Comparable<V>> Comparator<MapEntry!(K,V)> comparingByValue() { 991 // return (Comparator<MapEntry<K, V>> & Serializable) 992 // (c1, c2) -> c1.getValue().compareTo(c2.getValue()); 993 // } 994 995 /** 996 * Returns a comparator that compares {@link MapEntry} by key using the given 997 * {@link Comparator}. 998 * 999 * <p>The returned comparator is serializable if the specified comparator 1000 * is also serializable. 1001 * 1002 * @param !K the type of the map keys 1003 * @param !V the type of the map values 1004 * @param cmp the key {@link Comparator} 1005 * @return a comparator that compares {@link MapEntry} by the key. 1006 * @since 1.8 1007 */ 1008 // public static <K, V> Comparator<MapEntry<K, V>> comparingByKey(Comparator<K> cmp) { 1009 // Objects.requireNonNull(cmp); 1010 // return (Comparator<MapEntry<K, V>> & Serializable) 1011 // (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey()); 1012 // } 1013 1014 /** 1015 * Returns a comparator that compares {@link MapEntry} by value using the given 1016 * {@link Comparator}. 1017 * 1018 * <p>The returned comparator is serializable if the specified comparator 1019 * is also serializable. 1020 * 1021 * @param !K the type of the map keys 1022 * @param !V the type of the map values 1023 * @param cmp the value {@link Comparator} 1024 * @return a comparator that compares {@link MapEntry} by the value. 1025 * @since 1.8 1026 */ 1027 // public static <K, V> Comparator<MapEntry<K, V>> comparingByValue(Comparator<V> cmp) { 1028 // Objects.requireNonNull(cmp); 1029 // return (Comparator<MapEntry<K, V>> & Serializable) 1030 // (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue()); 1031 // } 1032 }